mirror of
https://github.com/QuasarApp/QtAndroidTools.git
synced 2025-04-26 21:14:31 +00:00
Imported changes from forked project https://github.com/auxility/better-apk-expansion
This commit is contained in:
parent
70a8fbf7be
commit
10e553aca9
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -110,4 +110,5 @@
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
</manifest>
|
||||
|
Loading…
x
Reference in New Issue
Block a user