summaryrefslogtreecommitdiffstats
path: root/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/packageinstaller/wear/WearPackageInstallerService.java')
-rw-r--r--src/com/android/packageinstaller/wear/WearPackageInstallerService.java244
1 files changed, 87 insertions, 157 deletions
diff --git a/src/com/android/packageinstaller/wear/WearPackageInstallerService.java b/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
index 7d5e0a6f..6435b76a 100644
--- a/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
+++ b/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
@@ -16,8 +16,10 @@
package com.android.packageinstaller.wear;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.app.Service;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.FeatureInfo;
@@ -26,9 +28,6 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -40,20 +39,19 @@ import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
-import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
import com.android.packageinstaller.DeviceUtils;
import com.android.packageinstaller.PackageUtil;
+import com.android.packageinstaller.R;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -84,22 +82,14 @@ import java.util.Set;
public class WearPackageInstallerService extends Service {
private static final String TAG = "WearPkgInstallerService";
- private static final String KEY_PACKAGE_NAME =
- "com.google.android.clockwork.EXTRA_PACKAGE_NAME";
- private static final String KEY_APP_LABEL = "com.google.android.clockwork.EXTRA_APP_LABEL";
- private static final String KEY_APP_ICON_URI =
- "com.google.android.clockwork.EXTRA_APP_ICON_URI";
- private static final String KEY_PERMS_LIST = "com.google.android.clockwork.EXTRA_PERMS_LIST";
- private static final String KEY_HAS_LAUNCHER =
- "com.google.android.clockwork.EXTRA_HAS_LAUNCHER";
-
- private static final String HOME_APP_PACKAGE_NAME = "com.google.android.wearable.app";
- private static final String SHOW_PERMS_SERVICE_CLASS =
- "com.google.android.clockwork.packagemanager.ShowPermsService";
+ private static final String WEAR_APPS_CHANNEL = "wear_app_install_uninstall";
private final int START_INSTALL = 1;
private final int START_UNINSTALL = 2;
+ private int mInstallNotificationId = 1;
+ private final Map<String, Integer> mNotifIdMap = new ArrayMap<>();
+
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
@@ -117,7 +107,7 @@ public class WearPackageInstallerService extends Service {
}
}
private ServiceHandler mServiceHandler;
-
+ private NotificationChannel mNotificationChannel;
private static volatile PowerManager.WakeLock lockStatic = null;
@Override
@@ -139,11 +129,13 @@ public class WearPackageInstallerService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
if (!DeviceUtils.isWear(this)) {
Log.w(TAG, "Not running on wearable.");
+ finishService(null, startId);
return START_NOT_STICKY;
}
if (intent == null) {
Log.w(TAG, "Got null intent.");
+ finishService(null, startId);
return START_NOT_STICKY;
}
@@ -154,11 +146,13 @@ public class WearPackageInstallerService extends Service {
Uri packageUri = intent.getData();
if (packageUri == null) {
Log.e(TAG, "No package URI in intent");
+ finishService(null, startId);
return START_NOT_STICKY;
}
final String packageName = WearPackageUtil.getSanitizedPackageName(packageUri);
if (packageName == null) {
Log.e(TAG, "Invalid package name in URI (expected package:<pkgName>): " + packageUri);
+ finishService(null, startId);
return START_NOT_STICKY;
}
@@ -173,15 +167,24 @@ public class WearPackageInstallerService extends Service {
}
WearPackageArgs.setStartId(intentBundle, startId);
WearPackageArgs.setPackageName(intentBundle, packageName);
+ String notifTitle;
if (Intent.ACTION_INSTALL_PACKAGE.equals(intent.getAction())) {
Message msg = mServiceHandler.obtainMessage(START_INSTALL);
msg.setData(intentBundle);
mServiceHandler.sendMessage(msg);
+ notifTitle = getString(R.string.installing);
} else if (Intent.ACTION_UNINSTALL_PACKAGE.equals(intent.getAction())) {
Message msg = mServiceHandler.obtainMessage(START_UNINSTALL);
msg.setData(intentBundle);
mServiceHandler.sendMessage(msg);
+ notifTitle = getString(R.string.uninstalling);
+ } else {
+ Log.e(TAG, "Unknown action : " + intent.getAction());
+ finishService(null, startId);
+ return START_NOT_STICKY;
}
+ Pair<Integer, Notification> notifPair = buildNotification(packageName, notifTitle);
+ startForeground(notifPair.first, notifPair.second);
return START_NOT_STICKY;
}
@@ -214,15 +217,15 @@ public class WearPackageInstallerService extends Service {
PackageInfo existingPkgInfo = null;
try {
existingPkgInfo = pm.getPackageInfo(packageName,
- PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS);
- if(existingPkgInfo != null) {
+ PackageManager.MATCH_ANY_USER | PackageManager.GET_PERMISSIONS);
+ if (existingPkgInfo != null) {
installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
}
} catch (PackageManager.NameNotFoundException e) {
// Ignore this exception. We could not find the package, will treat as a new
// installation.
}
- if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {
+ if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Replacing package:" + packageName);
}
@@ -241,7 +244,7 @@ public class WearPackageInstallerService extends Service {
Log.e(TAG, "Could not create a temp file from FD for " + packageName);
return;
}
- PackageParser.Package pkg = PackageUtil.getPackageInfo(tempFile);
+ PackageParser.Package pkg = PackageUtil.getPackageInfo(this, tempFile);
if (pkg == null) {
Log.e(TAG, "Could not parse apk information for " + packageName);
return;
@@ -253,6 +256,9 @@ public class WearPackageInstallerService extends Service {
return;
}
+ getLabelAndUpdateNotification(packageName,
+ getString(R.string.installing_app, pkg.applicationInfo.loadLabel(pm)));
+
List<String> wearablePerms = pkg.requestedPermissions;
// Log if the installed pkg has a higher version number.
@@ -357,14 +363,14 @@ public class WearPackageInstallerService extends Service {
PowerManager.WakeLock lock = getLock(this.getApplicationContext());
final PackageManager pm = getPackageManager();
try {
- // Result ignored.
- pm.getPackageInfo(packageName, 0);
+ PackageInfo pkgInfo = pm.getPackageInfo(packageName, 0);
+ getLabelAndUpdateNotification(packageName,
+ getString(R.string.uninstalling_app, pkgInfo.applicationInfo.loadLabel(pm)));
// Found package, send uninstall request.
pm.deletePackage(packageName, new PackageDeleteObserver(lock, startId),
PackageManager.DELETE_ALL_USERS);
- startPermsServiceForUninstall(packageName);
Log.i(TAG, "Sent delete request for " + packageName);
} catch (IllegalArgumentException | PackageManager.NameNotFoundException e) {
// Couldn't find the package, no need to call uninstall.
@@ -376,74 +382,50 @@ public class WearPackageInstallerService extends Service {
private boolean checkPermissions(PackageParser.Package pkg, int companionSdkVersion,
int companionDeviceVersion, Uri permUri, List<String> wearablePermissions,
File apkFile) {
- // If the Wear App is targeted for M-release, since the permission model has been changed,
- // permissions may not be granted on the phone yet. We need a different flow for user to
- // accept these permissions.
- //
- // Assumption: Code is running on E-release, so Wear is always running M.
- // - Case 1: If the Wear App(WA) is targeting 23, always choose the M model (4 cases)
- // - Case 2: Else if the Phone App(PA) is targeting 23 and Phone App(P) is running on M,
- // show a Dialog so that the user can accept all perms (1 case)
- // - Also show a warning to the developer if the watch is targeting M
- // - Case 3: If Case 2 is false, then the behavior on the phone is pre-M. Stick to pre-M
- // behavior on watch (as long as we don't hit case 1).
- // - 3a: WA(22) PA(22) P(22) -> watch app is not targeting 23
- // - 3b: WA(22) PA(22) P(23) -> watch app is not targeting 23
- // - 3c: WA(22) PA(23) P(22) -> watch app is not targeting 23
- // - Case 4: We did not get Companion App's/Device's version, always show dialog to user to
- // accept permissions. (This happens if the AndroidWear Companion App is really old).
- boolean isWearTargetingM =
- pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
- if (isWearTargetingM) { // Case 1
+ // Assumption: We are running on Android O.
+ // If the Phone App is targeting M, all permissions may not have been granted to the phone
+ // app. If the Wear App is then not targeting M, there may be permissions that are not
+ // granted on the Phone app (by the user) right now and we cannot just grant it for the Wear
+ // app.
+ if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
// Install the app if Wear App is ready for the new perms model.
return true;
}
- List<String> unavailableWearablePerms = getWearPermsNotGrantedOnPhone(pkg.packageName,
- permUri, wearablePermissions);
- if (unavailableWearablePerms == null) {
- return false;
- }
-
- if (unavailableWearablePerms.size() == 0) {
+ if (!doesWearHaveUngrantedPerms(pkg.packageName, permUri, wearablePermissions)) {
// All permissions requested by the watch are already granted on the phone, no need
// to do anything.
return true;
}
- // Cases 2 and 4.
- boolean isCompanionTargetingM = companionSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
- boolean isCompanionRunningM = companionDeviceVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
- if (isCompanionTargetingM) { // Case 2 Warning
- Log.w(TAG, "MNC: Wear app's targetSdkVersion should be at least 23, if " +
- "phone app is targeting at least 23, will continue.");
- }
- if ((isCompanionTargetingM && isCompanionRunningM) || // Case 2
- companionSdkVersion == 0 || companionDeviceVersion == 0) { // Case 4
- startPermsServiceForInstall(pkg, apkFile, unavailableWearablePerms);
+ // Log an error if Wear is targeting < 23 and phone is targeting >= 23.
+ if (companionSdkVersion == 0 || companionSdkVersion >= Build.VERSION_CODES.M) {
+ Log.e(TAG, "MNC: Wear app's targetSdkVersion should be at least 23, if "
+ + "phone app is targeting at least 23, will continue.");
}
- // Case 3a-3c.
return false;
}
/**
* Given a {@string packageName} corresponding to a phone app, query the provider for all the
* perms that are granted.
- * @return null if there is an error retrieving this info
- * else, a list of all the wearable perms that are not in the list of granted perms of
- * the phone.
+ *
+ * @return true if the Wear App has any perms that have not been granted yet on the phone side.
+ * @return true if there is any error cases.
*/
- private List<String> getWearPermsNotGrantedOnPhone(String packageName, Uri permUri,
+ private boolean doesWearHaveUngrantedPerms(String packageName, Uri permUri,
List<String> wearablePermissions) {
if (permUri == null) {
Log.e(TAG, "Permission URI is null");
- return null;
+ // Pretend there is an ungranted permission to avoid installing for error cases.
+ return true;
}
Cursor permCursor = getContentResolver().query(permUri, null, null, null, null);
if (permCursor == null) {
Log.e(TAG, "Could not get the cursor for the permissions");
- return null;
+ // Pretend there is an ungranted permission to avoid installing for error cases.
+ return true;
}
Set<String> grantedPerms = new HashSet<>();
@@ -465,10 +447,10 @@ public class WearPackageInstallerService extends Service {
}
permCursor.close();
- ArrayList<String> unavailableWearablePerms = new ArrayList<>();
+ boolean hasUngrantedPerm = false;
for (String wearablePerm : wearablePermissions) {
if (!grantedPerms.contains(wearablePerm)) {
- unavailableWearablePerms.add(wearablePerm);
+ hasUngrantedPerm = true;
if (!ungrantedPerms.contains(wearablePerm)) {
// This is an error condition. This means that the wearable has permissions that
// are not even declared in its host app. This is a developer error.
@@ -480,11 +462,11 @@ public class WearPackageInstallerService extends Service {
}
}
}
- return unavailableWearablePerms;
+ return hasUngrantedPerm;
}
private void finishService(PowerManager.WakeLock lock, int startId) {
- if (lock.isHeld()) {
+ if (lock != null && lock.isHeld()) {
lock.release();
}
stopSelf(startId);
@@ -501,86 +483,6 @@ public class WearPackageInstallerService extends Service {
return lockStatic;
}
- private void startPermsServiceForInstall(final PackageParser.Package pkg, final File apkFile,
- List<String> unavailableWearablePerms) {
- final String packageName = pkg.packageName;
-
- Intent showPermsIntent = new Intent()
- .setComponent(new ComponentName(HOME_APP_PACKAGE_NAME, SHOW_PERMS_SERVICE_CLASS))
- .setAction(Intent.ACTION_INSTALL_PACKAGE);
- final PackageManager pm = getPackageManager();
- pkg.applicationInfo.publicSourceDir = apkFile.getPath();
- final CharSequence label = pkg.applicationInfo.loadLabel(pm);
- final Uri iconUri = getIconFileUri(packageName, pkg.applicationInfo.loadIcon(pm));
- if (TextUtils.isEmpty(label) || iconUri == null) {
- Log.e(TAG, "MNC: Could not launch service since either label " + label +
- ", or icon Uri " + iconUri + " is invalid.");
- } else {
- showPermsIntent.putExtra(KEY_APP_LABEL, label);
- showPermsIntent.putExtra(KEY_APP_ICON_URI, iconUri);
- showPermsIntent.putExtra(KEY_PACKAGE_NAME, packageName);
- showPermsIntent.putExtra(KEY_PERMS_LIST,
- unavailableWearablePerms.toArray(new String[0]));
- showPermsIntent.putExtra(KEY_HAS_LAUNCHER, WearPackageUtil.hasLauncherActivity(pkg));
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "MNC: Launching Intent " + showPermsIntent + " for " + packageName +
- " with name " + label);
- }
- startService(showPermsIntent);
- }
- }
-
- private void startPermsServiceForUninstall(final String packageName) {
- Intent showPermsIntent = new Intent()
- .setComponent(new ComponentName(HOME_APP_PACKAGE_NAME, SHOW_PERMS_SERVICE_CLASS))
- .setAction(Intent.ACTION_UNINSTALL_PACKAGE);
- showPermsIntent.putExtra(KEY_PACKAGE_NAME, packageName);
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Launching Intent " + showPermsIntent + " for " + packageName);
- }
- startService(showPermsIntent);
- }
-
- private Uri getIconFileUri(final String packageName, final Drawable d) {
- if (d == null || !(d instanceof BitmapDrawable)) {
- Log.e(TAG, "Drawable is not a BitmapDrawable for " + packageName);
- return null;
- }
- File iconFile = WearPackageUtil.getIconFile(this, packageName);
-
- if (iconFile == null) {
- Log.e(TAG, "Could not get icon file for " + packageName);
- return null;
- }
-
- FileOutputStream fos = null;
- try {
- // Convert bitmap to byte array
- Bitmap bitmap = ((BitmapDrawable) d).getBitmap();
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
-
- // Write the bytes into the file
- fos = new FileOutputStream(iconFile);
- fos.write(bos.toByteArray());
- fos.flush();
-
- return WearPackageIconProvider.getUriForPackage(packageName);
- } catch (IOException e) {
- Log.e(TAG, "Could not convert drawable to icon file for package " + packageName, e);
- return null;
- } finally {
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException e) {
- // ignore
- }
- }
- }
- }
-
private class PackageInstallListener implements PackageInstallerImpl.InstallListener {
private Context mContext;
private PowerManager.WakeLock mWakeLock;
@@ -618,7 +520,6 @@ public class WearPackageInstallerService extends Service {
public void installFailed(int errorCode, String errorDesc) {
Log.e(TAG, "Package install failed " + mApplicationPackageName
+ ", errorCode " + errorCode);
- WearPackageUtil.removeFromPermStore(mContext, mApplicationPackageName);
finishService(mWakeLock, mStartId);
}
}
@@ -645,4 +546,33 @@ public class WearPackageInstallerService extends Service {
}
}
}
+
+ private synchronized Pair<Integer, Notification> buildNotification(final String packageName,
+ final String title) {
+ int notifId;
+ if (mNotifIdMap.containsKey(packageName)) {
+ notifId = mNotifIdMap.get(packageName);
+ } else {
+ notifId = mInstallNotificationId++;
+ mNotifIdMap.put(packageName, notifId);
+ }
+
+ if (mNotificationChannel == null) {
+ mNotificationChannel = new NotificationChannel(WEAR_APPS_CHANNEL,
+ getString(R.string.wear_app_channel), NotificationManager.IMPORTANCE_MIN);
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ notificationManager.createNotificationChannel(mNotificationChannel);
+ }
+ return new Pair<>(notifId, new Notification.Builder(this, WEAR_APPS_CHANNEL)
+ .setSmallIcon(R.drawable.ic_file_download)
+ .setContentTitle(title)
+ .build());
+ }
+
+ private void getLabelAndUpdateNotification(String packageName, String title) {
+ // Update notification since we have a label now.
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ Pair<Integer, Notification> notifPair = buildNotification(packageName, title);
+ notificationManager.notify(notifPair.first, notifPair.second);
+ }
}