• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.packageinstaller.wear;
18 
19 import android.app.Service;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.FeatureInfo;
24 import android.content.pm.IPackageDeleteObserver;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageParser;
28 import android.database.Cursor;
29 import android.graphics.Bitmap;
30 import android.graphics.drawable.BitmapDrawable;
31 import android.graphics.drawable.Drawable;
32 import android.net.Uri;
33 import android.os.Build;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.os.HandlerThread;
37 import android.os.IBinder;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.os.ParcelFileDescriptor;
41 import android.os.PowerManager;
42 import android.os.Process;
43 import android.text.TextUtils;
44 import android.util.Log;
45 
46 import com.android.packageinstaller.DeviceUtils;
47 import com.android.packageinstaller.PackageUtil;
48 
49 import java.io.ByteArrayOutputStream;
50 import java.io.File;
51 import java.io.FileNotFoundException;
52 import java.io.FileOutputStream;
53 import java.io.IOException;
54 import java.util.ArrayList;
55 import java.util.HashSet;
56 import java.util.List;
57 import java.util.Set;
58 
59 /**
60  * Service that will install/uninstall packages. It will check for permissions and features as well.
61  *
62  * -----------
63  *
64  * Debugging information:
65  *
66  *  Install Action example:
67  *  adb shell am startservice -a com.android.packageinstaller.wear.INSTALL_PACKAGE \
68  *     -d package://com.google.android.gms \
69  *     --eu com.google.android.clockwork.EXTRA_ASSET_URI content://com.google.android.clockwork.home.provider/host/com.google.android.wearable.app/wearable/com.google.android.gms/apk \
70  *     --es android.intent.extra.INSTALLER_PACKAGE_NAME com.google.android.gms \
71  *     --ez com.google.android.clockwork.EXTRA_CHECK_PERMS false \
72  *     --eu com.google.android.clockwork.EXTRA_PERM_URI content://com.google.android.clockwork.home.provider/host/com.google.android.wearable.app/permissions \
73  *     com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
74  *
75  *  Uninstall Action example:
76  *  adb shell am startservice -a com.android.packageinstaller.wear.UNINSTALL_PACKAGE \
77  *     -d package://com.google.android.gms \
78  *     com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
79  *
80  *  Retry GMS:
81  *  adb shell am startservice -a com.android.packageinstaller.wear.RETRY_GMS \
82  *     com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
83  */
84 public class WearPackageInstallerService extends Service {
85     private static final String TAG = "WearPkgInstallerService";
86 
87     private static final String KEY_PACKAGE_NAME =
88             "com.google.android.clockwork.EXTRA_PACKAGE_NAME";
89     private static final String KEY_APP_LABEL = "com.google.android.clockwork.EXTRA_APP_LABEL";
90     private static final String KEY_APP_ICON_URI =
91             "com.google.android.clockwork.EXTRA_APP_ICON_URI";
92     private static final String KEY_PERMS_LIST = "com.google.android.clockwork.EXTRA_PERMS_LIST";
93     private static final String KEY_HAS_LAUNCHER =
94             "com.google.android.clockwork.EXTRA_HAS_LAUNCHER";
95 
96     private static final String HOME_APP_PACKAGE_NAME = "com.google.android.wearable.app";
97     private static final String SHOW_PERMS_SERVICE_CLASS =
98             "com.google.android.clockwork.packagemanager.ShowPermsService";
99 
100     private final int START_INSTALL = 1;
101     private final int START_UNINSTALL = 2;
102 
103     private final class ServiceHandler extends Handler {
ServiceHandler(Looper looper)104         public ServiceHandler(Looper looper) {
105             super(looper);
106         }
107 
handleMessage(Message msg)108         public void handleMessage(Message msg) {
109             switch (msg.what) {
110                 case START_INSTALL:
111                     installPackage(msg.getData());
112                     break;
113                 case START_UNINSTALL:
114                     uninstallPackage(msg.getData());
115                     break;
116             }
117         }
118     }
119     private ServiceHandler mServiceHandler;
120 
121     private static volatile PowerManager.WakeLock lockStatic = null;
122 
123     @Override
onBind(Intent intent)124     public IBinder onBind(Intent intent) {
125         return null;
126     }
127 
128     @Override
onCreate()129     public void onCreate() {
130         super.onCreate();
131         HandlerThread thread = new HandlerThread("PackageInstallerThread",
132                 Process.THREAD_PRIORITY_BACKGROUND);
133         thread.start();
134 
135         mServiceHandler = new ServiceHandler(thread.getLooper());
136     }
137 
138     @Override
onStartCommand(Intent intent, int flags, int startId)139     public int onStartCommand(Intent intent, int flags, int startId) {
140         if (!DeviceUtils.isWear(this)) {
141             Log.w(TAG, "Not running on wearable.");
142             return START_NOT_STICKY;
143         }
144 
145         if (intent == null) {
146             Log.w(TAG, "Got null intent.");
147             return START_NOT_STICKY;
148         }
149 
150         if (Log.isLoggable(TAG, Log.DEBUG)) {
151             Log.d(TAG, "Got install/uninstall request " + intent);
152         }
153 
154         Uri packageUri = intent.getData();
155         if (packageUri == null) {
156             Log.e(TAG, "No package URI in intent");
157             return START_NOT_STICKY;
158         }
159         final String packageName = WearPackageUtil.getSanitizedPackageName(packageUri);
160         if (packageName == null) {
161             Log.e(TAG, "Invalid package name in URI (expected package:<pkgName>): " + packageUri);
162             return START_NOT_STICKY;
163         }
164 
165         PowerManager.WakeLock lock = getLock(this.getApplicationContext());
166         if (!lock.isHeld()) {
167             lock.acquire();
168         }
169 
170         Bundle intentBundle = intent.getExtras();
171         if (intentBundle == null) {
172             intentBundle = new Bundle();
173         }
174         WearPackageArgs.setStartId(intentBundle, startId);
175         WearPackageArgs.setPackageName(intentBundle, packageName);
176         if (Intent.ACTION_INSTALL_PACKAGE.equals(intent.getAction())) {
177             Message msg = mServiceHandler.obtainMessage(START_INSTALL);
178             msg.setData(intentBundle);
179             mServiceHandler.sendMessage(msg);
180         } else if (Intent.ACTION_UNINSTALL_PACKAGE.equals(intent.getAction())) {
181             Message msg = mServiceHandler.obtainMessage(START_UNINSTALL);
182             msg.setData(intentBundle);
183             mServiceHandler.sendMessage(msg);
184         }
185         return START_NOT_STICKY;
186     }
187 
installPackage(Bundle argsBundle)188     private void installPackage(Bundle argsBundle) {
189         int startId = WearPackageArgs.getStartId(argsBundle);
190         final String packageName = WearPackageArgs.getPackageName(argsBundle);
191         final Uri assetUri = WearPackageArgs.getAssetUri(argsBundle);
192         final Uri permUri = WearPackageArgs.getPermUri(argsBundle);
193         boolean checkPerms = WearPackageArgs.checkPerms(argsBundle);
194         boolean skipIfSameVersion = WearPackageArgs.skipIfSameVersion(argsBundle);
195         int companionSdkVersion = WearPackageArgs.getCompanionSdkVersion(argsBundle);
196         int companionDeviceVersion = WearPackageArgs.getCompanionDeviceVersion(argsBundle);
197         String compressionAlg = WearPackageArgs.getCompressionAlg(argsBundle);
198         boolean skipIfLowerVersion = WearPackageArgs.skipIfLowerVersion(argsBundle);
199 
200         if (Log.isLoggable(TAG, Log.DEBUG)) {
201             Log.d(TAG, "Installing package: " + packageName + ", assetUri: " + assetUri +
202                     ",permUri: " + permUri + ", startId: " + startId + ", checkPerms: " +
203                     checkPerms + ", skipIfSameVersion: " + skipIfSameVersion +
204                     ", compressionAlg: " + compressionAlg + ", companionSdkVersion: " +
205                     companionSdkVersion + ", companionDeviceVersion: " + companionDeviceVersion +
206                     ", skipIfLowerVersion: " + skipIfLowerVersion);
207         }
208         final PackageManager pm = getPackageManager();
209         File tempFile = null;
210         int installFlags = 0;
211         PowerManager.WakeLock lock = getLock(this.getApplicationContext());
212         boolean messageSent = false;
213         try {
214             PackageInfo existingPkgInfo = null;
215             try {
216                 existingPkgInfo = pm.getPackageInfo(packageName,
217                         PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS);
218                 if(existingPkgInfo != null) {
219                     installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
220                 }
221             } catch (PackageManager.NameNotFoundException e) {
222                 // Ignore this exception. We could not find the package, will treat as a new
223                 // installation.
224             }
225             if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {
226                 if (Log.isLoggable(TAG, Log.DEBUG)) {
227                     Log.d(TAG, "Replacing package:" + packageName);
228                 }
229             }
230             // TODO(28021618): This was left as a temp file due to the fact that this code is being
231             //       deprecated and that we need the bare minimum to continue working moving forward
232             //       If this code is used as reference, this permission logic might want to be
233             //       reworked to use a stream instead of a file so that we don't need to write a
234             //       file at all.  Note that there might be some trickiness with opening a stream
235             //       for multiple users.
236             ParcelFileDescriptor parcelFd = getContentResolver()
237                     .openFileDescriptor(assetUri, "r");
238             tempFile = WearPackageUtil.getFileFromFd(WearPackageInstallerService.this,
239                     parcelFd, packageName, compressionAlg);
240             if (tempFile == null) {
241                 Log.e(TAG, "Could not create a temp file from FD for " + packageName);
242                 return;
243             }
244             PackageParser.Package pkg = PackageUtil.getPackageInfo(tempFile);
245             if (pkg == null) {
246                 Log.e(TAG, "Could not parse apk information for " + packageName);
247                 return;
248             }
249 
250             if (!pkg.packageName.equals(packageName)) {
251                 Log.e(TAG, "Wearable Package Name has to match what is provided for " +
252                         packageName);
253                 return;
254             }
255 
256             List<String> wearablePerms = pkg.requestedPermissions;
257 
258             // Log if the installed pkg has a higher version number.
259             if (existingPkgInfo != null) {
260                 if (existingPkgInfo.versionCode == pkg.mVersionCode) {
261                     if (skipIfSameVersion) {
262                         Log.w(TAG, "Version number (" + pkg.mVersionCode +
263                                 ") of new app is equal to existing app for " + packageName +
264                                 "; not installing due to versionCheck");
265                         return;
266                     } else {
267                         Log.w(TAG, "Version number of new app (" + pkg.mVersionCode +
268                                 ") is equal to existing app for " + packageName);
269                     }
270                 } else if (existingPkgInfo.versionCode > pkg.mVersionCode) {
271                     if (skipIfLowerVersion) {
272                         // Starting in Feldspar, we are not going to allow downgrades of any app.
273                         Log.w(TAG, "Version number of new app (" + pkg.mVersionCode +
274                                 ") is lower than existing app ( " + existingPkgInfo.versionCode +
275                                 ") for " + packageName + "; not installing due to versionCheck");
276                         return;
277                     } else {
278                         Log.w(TAG, "Version number of new app (" + pkg.mVersionCode +
279                                 ") is lower than existing app ( " + existingPkgInfo.versionCode +
280                                 ") for " + packageName);
281                     }
282                 }
283 
284                 // Following the Android Phone model, we should only check for permissions for any
285                 // newly defined perms.
286                 if (existingPkgInfo.requestedPermissions != null) {
287                     for (int i = 0; i < existingPkgInfo.requestedPermissions.length; ++i) {
288                         // If the permission is granted, then we will not ask to request it again.
289                         if ((existingPkgInfo.requestedPermissionsFlags[i] &
290                                 PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
291                             if (Log.isLoggable(TAG, Log.DEBUG)) {
292                                 Log.d(TAG, existingPkgInfo.requestedPermissions[i] +
293                                         " is already granted for " + packageName);
294                             }
295                             wearablePerms.remove(existingPkgInfo.requestedPermissions[i]);
296                         }
297                     }
298                 }
299             }
300 
301             // Check that the wearable has all the features.
302             boolean hasAllFeatures = true;
303             if (pkg.reqFeatures != null) {
304                 for (FeatureInfo feature : pkg.reqFeatures) {
305                     if (feature.name != null && !pm.hasSystemFeature(feature.name) &&
306                             (feature.flags & FeatureInfo.FLAG_REQUIRED) != 0) {
307                         Log.e(TAG, "Wearable does not have required feature: " + feature +
308                                 " for " + packageName);
309                         hasAllFeatures = false;
310                     }
311                 }
312             }
313 
314             if (!hasAllFeatures) {
315                 return;
316             }
317 
318             // Check permissions on both the new wearable package and also on the already installed
319             // wearable package.
320             // If the app is targeting API level 23, we will also start a service in ClockworkHome
321             // which will ultimately prompt the user to accept/reject permissions.
322             if (checkPerms && !checkPermissions(pkg, companionSdkVersion, companionDeviceVersion,
323                     permUri, wearablePerms, tempFile)) {
324                 Log.w(TAG, "Wearable does not have enough permissions.");
325                 return;
326             }
327 
328             // Finally install the package.
329             ParcelFileDescriptor fd = getContentResolver().openFileDescriptor(assetUri, "r");
330             PackageInstallerFactory.getPackageInstaller(this).install(packageName, fd,
331                     new PackageInstallListener(this, lock, startId, packageName));
332 
333             messageSent = true;
334             Log.i(TAG, "Sent installation request for " + packageName);
335         } catch (FileNotFoundException e) {
336             Log.e(TAG, "Could not find the file with URI " + assetUri, e);
337         } finally {
338             if (!messageSent) {
339                 // Some error happened. If the message has been sent, we can wait for the observer
340                 // which will finish the service.
341                 if (tempFile != null) {
342                     tempFile.delete();
343                 }
344                 finishService(lock, startId);
345             }
346         }
347     }
348 
349     // TODO: This was left using the old PackageManager API due to the fact that this code is being
350     //       deprecated and that we need the bare minimum to continue working moving forward
351     //       If this code is used as reference, this logic should be reworked to use the new
352     //       PackageInstaller APIs similar to how installPackage was reworked
uninstallPackage(Bundle argsBundle)353     private void uninstallPackage(Bundle argsBundle) {
354         int startId = WearPackageArgs.getStartId(argsBundle);
355         final String packageName = WearPackageArgs.getPackageName(argsBundle);
356 
357         final PackageManager pm = getPackageManager();
358         try {
359             // Result ignored.
360             pm.getPackageInfo(packageName, 0);
361 
362             // Found package, send uninstall request.
363             PowerManager.WakeLock lock = getLock(this.getApplicationContext());
364 
365             try {
366                 pm.deletePackage(packageName, new PackageDeleteObserver(lock, startId),
367                         PackageManager.DELETE_ALL_USERS);
368             } catch (IllegalArgumentException e) {
369                 // Couldn't find the package, no need to call uninstall.
370                 Log.w(TAG, "Could not find package, not deleting " + packageName, e);
371             }
372 
373             startPermsServiceForUninstall(packageName);
374             Log.i(TAG, "Sent delete request for " + packageName);
375         } catch (PackageManager.NameNotFoundException e) {
376             // Couldn't find the package, no need to call uninstall.
377             Log.w(TAG, "Could not find package, not deleting " + packageName, e);
378         }
379     }
380 
checkPermissions(PackageParser.Package pkg, int companionSdkVersion, int companionDeviceVersion, Uri permUri, List<String> wearablePermissions, File apkFile)381     private boolean checkPermissions(PackageParser.Package pkg, int companionSdkVersion,
382             int companionDeviceVersion, Uri permUri, List<String> wearablePermissions,
383             File apkFile) {
384         // If the Wear App is targeted for M-release, since the permission model has been changed,
385         // permissions may not be granted on the phone yet. We need a different flow for user to
386         // accept these permissions.
387         //
388         // Assumption: Code is running on E-release, so Wear is always running M.
389         // - Case 1: If the Wear App(WA) is targeting 23, always choose the M model (4 cases)
390         // - Case 2: Else if the Phone App(PA) is targeting 23 and Phone App(P) is running on M,
391         // show a Dialog so that the user can accept all perms (1 case)
392         //   - Also show a warning to the developer if the watch is targeting M
393         // - Case 3: If Case 2 is false, then the behavior on the phone is pre-M. Stick to pre-M
394         // behavior on watch (as long as we don't hit case 1).
395         //   - 3a: WA(22) PA(22) P(22) -> watch app is not targeting 23
396         //   - 3b: WA(22) PA(22) P(23) -> watch app is not targeting 23
397         //   - 3c: WA(22) PA(23) P(22) -> watch app is not targeting 23
398         // - Case 4: We did not get Companion App's/Device's version, always show dialog to user to
399         // accept permissions. (This happens if the AndroidWear Companion App is really old).
400         boolean isWearTargetingM =
401                 pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
402         if (isWearTargetingM) { // Case 1
403             // Install the app if Wear App is ready for the new perms model.
404             return true;
405         }
406 
407         List<String> unavailableWearablePerms = getWearPermsNotGrantedOnPhone(pkg.packageName,
408                 permUri, wearablePermissions);
409         if (unavailableWearablePerms == null) {
410             return false;
411         }
412 
413         if (unavailableWearablePerms.size() == 0) {
414             // All permissions requested by the watch are already granted on the phone, no need
415             // to do anything.
416             return true;
417         }
418 
419         // Cases 2 and 4.
420         boolean isCompanionTargetingM = companionSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
421         boolean isCompanionRunningM = companionDeviceVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
422         if (isCompanionTargetingM) { // Case 2 Warning
423             Log.w(TAG, "MNC: Wear app's targetSdkVersion should be at least 23, if " +
424                     "phone app is targeting at least 23, will continue.");
425         }
426         if ((isCompanionTargetingM && isCompanionRunningM) || // Case 2
427                 companionSdkVersion == 0 || companionDeviceVersion == 0) { // Case 4
428             startPermsServiceForInstall(pkg, apkFile, unavailableWearablePerms);
429         }
430 
431         // Case 3a-3c.
432         return false;
433     }
434 
435     /**
436      * Given a {@string packageName} corresponding to a phone app, query the provider for all the
437      * perms that are granted.
438      * @return null if there is an error retrieving this info
439      *         else, a list of all the wearable perms that are not in the list of granted perms of
440      * the phone.
441      */
getWearPermsNotGrantedOnPhone(String packageName, Uri permUri, List<String> wearablePermissions)442     private List<String> getWearPermsNotGrantedOnPhone(String packageName, Uri permUri,
443             List<String> wearablePermissions) {
444         if (permUri == null) {
445             Log.e(TAG, "Permission URI is null");
446             return null;
447         }
448         Cursor permCursor = getContentResolver().query(permUri, null, null, null, null);
449         if (permCursor == null) {
450             Log.e(TAG, "Could not get the cursor for the permissions");
451             return null;
452         }
453 
454         Set<String> grantedPerms = new HashSet<>();
455         Set<String> ungrantedPerms = new HashSet<>();
456         while(permCursor.moveToNext()) {
457             // Make sure that the MatrixCursor returned by the ContentProvider has 2 columns and
458             // verify their types.
459             if (permCursor.getColumnCount() == 2
460                     && Cursor.FIELD_TYPE_STRING == permCursor.getType(0)
461                     && Cursor.FIELD_TYPE_INTEGER == permCursor.getType(1)) {
462                 String perm = permCursor.getString(0);
463                 Integer granted = permCursor.getInt(1);
464                 if (granted == 1) {
465                     grantedPerms.add(perm);
466                 } else {
467                     ungrantedPerms.add(perm);
468                 }
469             }
470         }
471         permCursor.close();
472 
473         ArrayList<String> unavailableWearablePerms = new ArrayList<>();
474         for (String wearablePerm : wearablePermissions) {
475             if (!grantedPerms.contains(wearablePerm)) {
476                 unavailableWearablePerms.add(wearablePerm);
477                 if (!ungrantedPerms.contains(wearablePerm)) {
478                     // This is an error condition. This means that the wearable has permissions that
479                     // are not even declared in its host app. This is a developer error.
480                     Log.e(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm
481                             + "\" that is not defined in the host application's manifest.");
482                 } else {
483                     Log.w(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm +
484                             "\" that is not granted in the host application.");
485                 }
486             }
487         }
488         return unavailableWearablePerms;
489     }
490 
finishService(PowerManager.WakeLock lock, int startId)491     private void finishService(PowerManager.WakeLock lock, int startId) {
492         if (lock.isHeld()) {
493             lock.release();
494         }
495         stopSelf(startId);
496     }
497 
getLock(Context context)498     private synchronized PowerManager.WakeLock getLock(Context context) {
499         if (lockStatic == null) {
500             PowerManager mgr =
501                     (PowerManager) context.getSystemService(Context.POWER_SERVICE);
502             lockStatic = mgr.newWakeLock(
503                     PowerManager.PARTIAL_WAKE_LOCK, context.getClass().getSimpleName());
504             lockStatic.setReferenceCounted(true);
505         }
506         return lockStatic;
507     }
508 
startPermsServiceForInstall(final PackageParser.Package pkg, final File apkFile, List<String> unavailableWearablePerms)509     private void startPermsServiceForInstall(final PackageParser.Package pkg, final File apkFile,
510             List<String> unavailableWearablePerms) {
511         final String packageName = pkg.packageName;
512 
513         Intent showPermsIntent = new Intent()
514                 .setComponent(new ComponentName(HOME_APP_PACKAGE_NAME, SHOW_PERMS_SERVICE_CLASS))
515                 .setAction(Intent.ACTION_INSTALL_PACKAGE);
516         final PackageManager pm = getPackageManager();
517         pkg.applicationInfo.publicSourceDir = apkFile.getPath();
518         final CharSequence label = pkg.applicationInfo.loadLabel(pm);
519         final Uri iconUri = getIconFileUri(packageName, pkg.applicationInfo.loadIcon(pm));
520         if (TextUtils.isEmpty(label) || iconUri == null) {
521             Log.e(TAG, "MNC: Could not launch service since either label " + label +
522                     ", or icon Uri " + iconUri + " is invalid.");
523         } else {
524             showPermsIntent.putExtra(KEY_APP_LABEL, label);
525             showPermsIntent.putExtra(KEY_APP_ICON_URI, iconUri);
526             showPermsIntent.putExtra(KEY_PACKAGE_NAME, packageName);
527             showPermsIntent.putExtra(KEY_PERMS_LIST,
528                     unavailableWearablePerms.toArray(new String[0]));
529             showPermsIntent.putExtra(KEY_HAS_LAUNCHER, WearPackageUtil.hasLauncherActivity(pkg));
530 
531             if (Log.isLoggable(TAG, Log.DEBUG)) {
532                 Log.d(TAG, "MNC: Launching Intent " + showPermsIntent + " for " + packageName +
533                         " with name " + label);
534             }
535             startService(showPermsIntent);
536         }
537     }
538 
startPermsServiceForUninstall(final String packageName)539     private void startPermsServiceForUninstall(final String packageName) {
540         Intent showPermsIntent = new Intent()
541                 .setComponent(new ComponentName(HOME_APP_PACKAGE_NAME, SHOW_PERMS_SERVICE_CLASS))
542                 .setAction(Intent.ACTION_UNINSTALL_PACKAGE);
543         showPermsIntent.putExtra(KEY_PACKAGE_NAME, packageName);
544         if (Log.isLoggable(TAG, Log.DEBUG)) {
545             Log.d(TAG, "Launching Intent " + showPermsIntent + " for " + packageName);
546         }
547         startService(showPermsIntent);
548     }
549 
getIconFileUri(final String packageName, final Drawable d)550     private Uri getIconFileUri(final String packageName, final Drawable d) {
551         if (d == null || !(d instanceof BitmapDrawable)) {
552             Log.e(TAG, "Drawable is not a BitmapDrawable for " + packageName);
553             return null;
554         }
555         File iconFile = WearPackageUtil.getIconFile(this, packageName);
556 
557         if (iconFile == null) {
558             Log.e(TAG, "Could not get icon file for " + packageName);
559             return null;
560         }
561 
562         FileOutputStream fos = null;
563         try {
564             // Convert bitmap to byte array
565             Bitmap bitmap = ((BitmapDrawable) d).getBitmap();
566             ByteArrayOutputStream bos = new ByteArrayOutputStream();
567             bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
568 
569             // Write the bytes into the file
570             fos = new FileOutputStream(iconFile);
571             fos.write(bos.toByteArray());
572             fos.flush();
573 
574             return WearPackageIconProvider.getUriForPackage(packageName);
575         } catch (IOException e) {
576             Log.e(TAG, "Could not convert drawable to icon file for package " + packageName, e);
577             return null;
578         } finally {
579             if (fos != null) {
580                 try {
581                     fos.close();
582                 } catch (IOException e) {
583                     // ignore
584                 }
585             }
586         }
587     }
588 
589     private class PackageInstallListener implements PackageInstallerImpl.InstallListener {
590         private Context mContext;
591         private PowerManager.WakeLock mWakeLock;
592         private int mStartId;
593         private String mApplicationPackageName;
PackageInstallListener(Context context, PowerManager.WakeLock wakeLock, int startId, String applicationPackageName)594         private PackageInstallListener(Context context, PowerManager.WakeLock wakeLock,
595                 int startId, String applicationPackageName) {
596             mContext = context;
597             mWakeLock = wakeLock;
598             mStartId = startId;
599             mApplicationPackageName = applicationPackageName;
600         }
601 
602         @Override
installBeginning()603         public void installBeginning() {
604             Log.i(TAG, "Package " + mApplicationPackageName + " is being installed.");
605         }
606 
607         @Override
installSucceeded()608         public void installSucceeded() {
609             try {
610                 Log.i(TAG, "Package " + mApplicationPackageName + " was installed.");
611 
612                 // Delete tempFile from the file system.
613                 File tempFile = WearPackageUtil.getTemporaryFile(mContext, mApplicationPackageName);
614                 if (tempFile != null) {
615                     tempFile.delete();
616                 }
617             } finally {
618                 finishService(mWakeLock, mStartId);
619             }
620         }
621 
622         @Override
installFailed(int errorCode, String errorDesc)623         public void installFailed(int errorCode, String errorDesc) {
624             Log.e(TAG, "Package install failed " + mApplicationPackageName
625                     + ", errorCode " + errorCode);
626             WearPackageUtil.removeFromPermStore(mContext, mApplicationPackageName);
627             finishService(mWakeLock, mStartId);
628         }
629     }
630 
631     private class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
632         private PowerManager.WakeLock mWakeLock;
633         private int mStartId;
634 
PackageDeleteObserver(PowerManager.WakeLock wakeLock, int startId)635         private PackageDeleteObserver(PowerManager.WakeLock wakeLock, int startId) {
636             mWakeLock = wakeLock;
637             mStartId = startId;
638         }
639 
packageDeleted(String packageName, int returnCode)640         public void packageDeleted(String packageName, int returnCode) {
641             try {
642                 if (returnCode >= 0) {
643                     Log.i(TAG, "Package " + packageName + " was uninstalled.");
644                 } else {
645                     Log.e(TAG, "Package uninstall failed " + packageName + ", returnCode " +
646                             returnCode);
647                 }
648             } finally {
649                 finishService(mWakeLock, mStartId);
650             }
651         }
652     }
653 }
654