From 10e553aca9b5f75245816706313f65c3a251299e Mon Sep 17 00:00:00 2001 From: FalsinSoft Date: Tue, 29 Sep 2020 22:47:30 +0200 Subject: [PATCH] Imported changes from forked project https://github.com/auxility/better-apk-expansion --- .../vending/expansion/downloader/Helpers.java | 19 +- .../downloader/impl/DownloadNotification.java | 12 +- .../downloader/impl/DownloaderService.java | 76 ++++--- .../downloader/impl/HttpDateTime.java | 200 ------------------ .../android/AndroidManifest.xml | 1 + 5 files changed, 73 insertions(+), 235 deletions(-) delete mode 100644 QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/HttpDateTime.java diff --git a/QtAndroidTools/src/com/google/android/vending/expansion/downloader/Helpers.java b/QtAndroidTools/src/com/google/android/vending/expansion/downloader/Helpers.java index 1174256..d5d1e08 100644 --- a/QtAndroidTools/src/com/google/android/vending/expansion/downloader/Helpers.java +++ b/QtAndroidTools/src/com/google/android/vending/expansion/downloader/Helpers.java @@ -25,7 +25,6 @@ import android.os.SystemClock; import androidx.annotation.StringRes; import android.util.Log; -//import com.android.vending.expansion.downloader.R; import com.falsinsoft.qtandroidtools.AndroidApkExpansionFiles; import java.io.File; @@ -40,6 +39,7 @@ import java.util.regex.Pattern; /** * Some helper functions for the download manager */ +@SuppressWarnings("unused") public class Helpers { public static Random sRandom = new Random(SystemClock.uptimeMillis()); @@ -225,13 +225,18 @@ public class Helpers { // This technically existed since Honeycomb, but it is critical // on KitKat and greater versions since it will create the // directory if needed - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - return c.getObbDir().toString(); - } else { - File root = Environment.getExternalStorageDirectory(); - String path = root.toString() + Constants.EXP_PATH + c.getPackageName(); - return path; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + { + final File obbDir = c.getObbDir(); + + if (obbDir != null ) // It really can return null in some cases. So, if it's null - go to old fallback mechanism... + { + return obbDir.toString(); + } } + + File root = Environment.getExternalStorageDirectory(); + return root.toString() + Constants.EXP_PATH + c.getPackageName(); } /** diff --git a/QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java b/QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java index 277c5bb..7a2d09f 100644 --- a/QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java +++ b/QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/DownloadNotification.java @@ -16,13 +16,13 @@ package com.google.android.vending.expansion.downloader.impl; +import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; -//import com.android.vending.expansion.downloader.R; import com.falsinsoft.qtandroidtools.AndroidApkExpansionFiles; import com.google.android.vending.expansion.downloader.DownloadProgressInfo; @@ -40,6 +40,7 @@ import com.google.android.vending.expansion.downloader.IDownloaderClient; * The application interface for the downloader also needs to understand and * handle these transient states. */ +@SuppressWarnings("unused") class DownloadNotification { private int mState; @@ -71,8 +72,9 @@ class DownloadNotification { mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mClientProxy = new ClientProxy(ctx); - mActiveDownloadBuilder = new NotificationCompat.Builder(ctx); - mBuilder = new NotificationCompat.Builder(ctx); + // Passing empty string as a channel ID is just a hack against deprecation, channel ID will be set later. + mActiveDownloadBuilder = new NotificationCompat.Builder(ctx, ""); + mBuilder = new NotificationCompat.Builder(ctx, ""); // Set Notification category and priorities to something that makes sense for a long // lived background task. @@ -89,6 +91,10 @@ class DownloadNotification { return mContentIntent; } + public int getNotificationId() { return NOTIFICATION_ID; } + + public Notification buildCurrentNotification() { return mCurrentBuilder.build(); } + public void setClientIntent(PendingIntent clientIntent) { this.mBuilder.setContentIntent(clientIntent); this.mActiveDownloadBuilder.setContentIntent(clientIntent); diff --git a/QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java b/QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java index 3fc6edf..39273a0 100644 --- a/QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java +++ b/QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/DownloaderService.java @@ -46,6 +46,7 @@ import java.io.File; * Note that Android by default will kill off any process that has an open file * handle on the shared (SD Card) partition if the partition is unmounted. */ +@SuppressWarnings("unused") public class DownloaderService extends CustomIntentService implements IDownloaderService { public DownloaderService() { @@ -377,6 +378,13 @@ public class DownloaderService extends CustomIntentService implements IDownloade */ private static boolean sIsRunning; + /** + * Service parameters + */ + String mChannelId; + byte[] mSalt; + String mPublicKey; + @Override public IBinder onBind(Intent paramIntent) { Log.d(Constants.TAG, "Service Bound"); @@ -588,10 +596,7 @@ public class DownloaderService extends CustomIntentService implements IDownloade private static boolean isLVLCheckRequired(DownloadsDB db, PackageInfo pi) { // we need to update the LVL check and get a successful status to // proceed - if (db.mVersionCode != pi.versionCode) { - return true; - } - return false; + return db.mVersionCode != pi.versionCode; } /** @@ -672,7 +677,11 @@ public class DownloaderService extends CustomIntentService implements IDownloade downloadIntent.putExtra(EXTRA_CHANNEL_ID, channelId); downloadIntent.putExtra(EXTRA_SALT, salt); downloadIntent.putExtra(EXTRA_PUBLIC_KEY, publicKey); - context.startService(downloadIntent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(downloadIntent); + } else { + context.startService(downloadIntent); + } break; } return status; @@ -702,7 +711,15 @@ public class DownloaderService extends CustomIntentService implements IDownloade } Intent fileIntent = new Intent(this, this.getClass()); fileIntent.putExtra(EXTRA_PENDING_INTENT, mPendingIntent); - this.startService(fileIntent); + fileIntent.putExtra(EXTRA_CHANNEL_ID, mChannelId); + fileIntent.putExtra(EXTRA_SALT, mSalt); + fileIntent.putExtra(EXTRA_PUBLIC_KEY, mPublicKey); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + this.startForegroundService(fileIntent); + } else { + this.startService(fileIntent); + } } private class LVLRunnable implements Runnable { @@ -911,7 +928,7 @@ public class DownloaderService extends CustomIntentService implements IDownloade return !Helpers.doesFileExist(this, filename, fileSize, true); } - private void scheduleAlarm(long wakeUp, boolean repeated, Bundle callerExtras) { + private void scheduleAlarm(long wakeUp, boolean repeated, Intent originalIntent) { AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE); if (alarms == null) { Log.e(Constants.TAG, "couldn't get alarm manager"); @@ -925,7 +942,7 @@ public class DownloaderService extends CustomIntentService implements IDownloade // put original extras to the wake up intent Intent intent = new Intent(this, AlarmReceiver.class); intent.setAction(Constants.ACTION_RETRY); - intent.putExtras(callerExtras); + intent.putExtras(originalIntent); mAlarmIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); @@ -966,13 +983,19 @@ public class DownloaderService extends CustomIntentService implements IDownloade @Override public void onReceive(Context context, Intent intent) { pollNetworkState(); - if (mStateChanged - && !isServiceRunning()) { + if (mStateChanged && !isServiceRunning()) { Log.d(Constants.TAG, "InnerBroadcastReceiver Called"); Intent fileIntent = new Intent(context, mService.getClass()); fileIntent.putExtra(EXTRA_PENDING_INTENT, mPendingIntent); + fileIntent.putExtra(EXTRA_CHANNEL_ID, mChannelId); + fileIntent.putExtra(EXTRA_SALT, mSalt); + fileIntent.putExtra(EXTRA_PUBLIC_KEY, mPublicKey); // send a new intent to the service - context.startService(fileIntent); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(fileIntent); + } else { + context.startService(fileIntent); + } } } } @@ -984,7 +1007,7 @@ public class DownloaderService extends CustomIntentService implements IDownloade @Override public void onReceive(Context context, Intent intent) { try { - final PendingIntent pendingIntent = (PendingIntent) intent + final PendingIntent pendingIntent = intent .getParcelableExtra(EXTRA_PENDING_INTENT); startDownloadServiceIfRequired( @@ -996,7 +1019,9 @@ public class DownloaderService extends CustomIntentService implements IDownloade ); } catch (PackageManager.NameNotFoundException e) { - Log.e(getClass().getSimpleName(), "onReceive: ", e); + if (Constants.LOGVV) { + Log.e(getClass().getSimpleName(), "onReceive: ", e); + } } } } @@ -1013,11 +1038,11 @@ public class DownloaderService extends CustomIntentService implements IDownloade // and download status when the instance is created DownloadsDB db = DownloadsDB.getDB(this); final PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT); - final String channelId = intent.getStringExtra(EXTRA_CHANNEL_ID); - final byte[] salt = intent.getByteArrayExtra(EXTRA_SALT); - final String publicKey = intent.getStringExtra(EXTRA_PUBLIC_KEY); + mChannelId = intent.getStringExtra(EXTRA_CHANNEL_ID); + mSalt = intent.getByteArrayExtra(EXTRA_SALT); + mPublicKey = intent.getStringExtra(EXTRA_PUBLIC_KEY); - mNotification.setChannelId(channelId); + mNotification.setChannelId(mChannelId); if (null != pendingIntent) { mNotification.setClientIntent(pendingIntent); @@ -1029,10 +1054,14 @@ public class DownloaderService extends CustomIntentService implements IDownloade return; } + // Critical to use on Android O (API 26+) or else service will be killed by ANR within 5 seconds! + // See here: https://stackoverflow.com/questions/44425584/context-startforegroundservice-did-not-then-call-service-startforeground + startForeground(mNotification.getNotificationId(), mNotification.buildCurrentNotification()); + // when the LVL check completes, a successful response will update // the service if (isLVLCheckRequired(db, mPackageInfo)) { - updateLVL(this, channelId, salt, publicKey); + updateLVL(this, mChannelId, mSalt, mPublicKey); return; } @@ -1079,7 +1108,7 @@ public class DownloaderService extends CustomIntentService implements IDownloade DownloadThread dt = new DownloadThread(info, this, mNotification); cancelAlarms(); // schedule repeated alarm to check if process is alive - scheduleAlarm(Constants.ACTIVE_THREAD_WATCHDOG, true, intent.getExtras()); + scheduleAlarm(Constants.ACTIVE_THREAD_WATCHDOG, true, intent); dt.run(); cancelAlarms(); } @@ -1089,7 +1118,7 @@ public class DownloaderService extends CustomIntentService implements IDownloade switch (info.mStatus) { case STATUS_FORBIDDEN: // the URL is out of date - updateLVL(this, channelId, salt, publicKey); + updateLVL(this, mChannelId, mSalt, mPublicKey); return; case STATUS_SUCCESS: mBytesSoFar += info.mCurrentBytes - startingCount; @@ -1144,7 +1173,7 @@ public class DownloaderService extends CustomIntentService implements IDownloade break; } if (setWakeWatchdog) { - scheduleAlarm(Constants.WATCHDOG_WAKE_TIMER, false, intent.getExtras()); + scheduleAlarm(Constants.WATCHDOG_WAKE_TIMER, false, intent); } else { cancelAlarms(); } @@ -1325,10 +1354,7 @@ public class DownloaderService extends CustomIntentService implements IDownloade // the database automatically reads the metadata for version code // and download status when the instance is created DownloadsDB db = DownloadsDB.getDB(this); - if (db.mStatus == 0) { - return true; - } - return false; + return db.mStatus == 0; } @Override diff --git a/QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/HttpDateTime.java b/QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/HttpDateTime.java deleted file mode 100644 index 3f440e9..0000000 --- a/QtAndroidTools/src/com/google/android/vending/expansion/downloader/impl/HttpDateTime.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.vending.expansion.downloader.impl; - -import android.text.format.Time; - -import java.util.Calendar; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Helper for parsing an HTTP date. - */ -public final class HttpDateTime { - - /* - * Regular expression for parsing HTTP-date. Wdy, DD Mon YYYY HH:MM:SS GMT - * RFC 822, updated by RFC 1123 Weekday, DD-Mon-YY HH:MM:SS GMT RFC 850, - * obsoleted by RFC 1036 Wdy Mon DD HH:MM:SS YYYY ANSI C's asctime() format - * with following variations Wdy, DD-Mon-YYYY HH:MM:SS GMT Wdy, (SP)D Mon - * YYYY HH:MM:SS GMT Wdy,DD Mon YYYY HH:MM:SS GMT Wdy, DD-Mon-YY HH:MM:SS - * GMT Wdy, DD Mon YYYY HH:MM:SS -HHMM Wdy, DD Mon YYYY HH:MM:SS Wdy Mon - * (SP)D HH:MM:SS YYYY Wdy Mon DD HH:MM:SS YYYY GMT HH can be H if the first - * digit is zero. Mon can be the full name of the month. - */ - private static final String HTTP_DATE_RFC_REGEXP = - "([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]" - + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])"; - - private static final String HTTP_DATE_ANSIC_REGEXP = - "[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]" - + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})"; - - /** - * The compiled version of the HTTP-date regular expressions. - */ - private static final Pattern HTTP_DATE_RFC_PATTERN = - Pattern.compile(HTTP_DATE_RFC_REGEXP); - private static final Pattern HTTP_DATE_ANSIC_PATTERN = - Pattern.compile(HTTP_DATE_ANSIC_REGEXP); - - private static class TimeOfDay { - TimeOfDay(int h, int m, int s) { - this.hour = h; - this.minute = m; - this.second = s; - } - - int hour; - int minute; - int second; - } - - public static long parse(String timeString) - throws IllegalArgumentException { - - int date = 1; - int month = Calendar.JANUARY; - int year = 1970; - TimeOfDay timeOfDay; - - Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString); - if (rfcMatcher.find()) { - date = getDate(rfcMatcher.group(1)); - month = getMonth(rfcMatcher.group(2)); - year = getYear(rfcMatcher.group(3)); - timeOfDay = getTime(rfcMatcher.group(4)); - } else { - Matcher ansicMatcher = HTTP_DATE_ANSIC_PATTERN.matcher(timeString); - if (ansicMatcher.find()) { - month = getMonth(ansicMatcher.group(1)); - date = getDate(ansicMatcher.group(2)); - timeOfDay = getTime(ansicMatcher.group(3)); - year = getYear(ansicMatcher.group(4)); - } else { - throw new IllegalArgumentException(); - } - } - - // FIXME: Y2038 BUG! - if (year >= 2038) { - year = 2038; - month = Calendar.JANUARY; - date = 1; - } - - Time time = new Time(Time.TIMEZONE_UTC); - time.set(timeOfDay.second, timeOfDay.minute, timeOfDay.hour, date, - month, year); - return time.toMillis(false /* use isDst */); - } - - private static int getDate(String dateString) { - if (dateString.length() == 2) { - return (dateString.charAt(0) - '0') * 10 - + (dateString.charAt(1) - '0'); - } else { - return (dateString.charAt(0) - '0'); - } - } - - /* - * jan = 9 + 0 + 13 = 22 feb = 5 + 4 + 1 = 10 mar = 12 + 0 + 17 = 29 apr = 0 - * + 15 + 17 = 32 may = 12 + 0 + 24 = 36 jun = 9 + 20 + 13 = 42 jul = 9 + 20 - * + 11 = 40 aug = 0 + 20 + 6 = 26 sep = 18 + 4 + 15 = 37 oct = 14 + 2 + 19 - * = 35 nov = 13 + 14 + 21 = 48 dec = 3 + 4 + 2 = 9 - */ - private static int getMonth(String monthString) { - int hash = Character.toLowerCase(monthString.charAt(0)) + - Character.toLowerCase(monthString.charAt(1)) + - Character.toLowerCase(monthString.charAt(2)) - 3 * 'a'; - switch (hash) { - case 22: - return Calendar.JANUARY; - case 10: - return Calendar.FEBRUARY; - case 29: - return Calendar.MARCH; - case 32: - return Calendar.APRIL; - case 36: - return Calendar.MAY; - case 42: - return Calendar.JUNE; - case 40: - return Calendar.JULY; - case 26: - return Calendar.AUGUST; - case 37: - return Calendar.SEPTEMBER; - case 35: - return Calendar.OCTOBER; - case 48: - return Calendar.NOVEMBER; - case 9: - return Calendar.DECEMBER; - default: - throw new IllegalArgumentException(); - } - } - - private static int getYear(String yearString) { - if (yearString.length() == 2) { - int year = (yearString.charAt(0) - '0') * 10 - + (yearString.charAt(1) - '0'); - if (year >= 70) { - return year + 1900; - } else { - return year + 2000; - } - } else if (yearString.length() == 3) { - // According to RFC 2822, three digit years should be added to 1900. - int year = (yearString.charAt(0) - '0') * 100 - + (yearString.charAt(1) - '0') * 10 - + (yearString.charAt(2) - '0'); - return year + 1900; - } else if (yearString.length() == 4) { - return (yearString.charAt(0) - '0') * 1000 - + (yearString.charAt(1) - '0') * 100 - + (yearString.charAt(2) - '0') * 10 - + (yearString.charAt(3) - '0'); - } else { - return 1970; - } - } - - private static TimeOfDay getTime(String timeString) { - // HH might be H - int i = 0; - int hour = timeString.charAt(i++) - '0'; - if (timeString.charAt(i) != ':') - hour = hour * 10 + (timeString.charAt(i++) - '0'); - // Skip ':' - i++; - - int minute = (timeString.charAt(i++) - '0') * 10 - + (timeString.charAt(i++) - '0'); - // Skip ':' - i++; - - int second = (timeString.charAt(i++) - '0') * 10 - + (timeString.charAt(i++) - '0'); - - return new TimeOfDay(hour, minute, second); - } -} diff --git a/QtAndroidToolsDemo/android/AndroidManifest.xml b/QtAndroidToolsDemo/android/AndroidManifest.xml index cfd12d7..a786a20 100644 --- a/QtAndroidToolsDemo/android/AndroidManifest.xml +++ b/QtAndroidToolsDemo/android/AndroidManifest.xml @@ -110,4 +110,5 @@ +