This commit is contained in:
FalsinSoft 2020-09-29 22:47:30 +02:00
parent 70a8fbf7be
commit 10e553aca9
5 changed files with 73 additions and 235 deletions

View File

@ -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();
}
/**

View File

@ -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);

View File

@ -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

View File

@ -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);
}
}

View File

@ -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>