• 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 permissions on both the new wearable package and also on the already installed
302             // wearable package.
303             // If the app is targeting API level 23, we will also start a service in ClockworkHome
304             // which will ultimately prompt the user to accept/reject permissions.
305             if (checkPerms && !checkPermissions(pkg, companionSdkVersion, companionDeviceVersion,
306                     permUri, wearablePerms, tempFile)) {
307                 Log.w(TAG, "Wearable does not have enough permissions.");
308                 return;
309             }
310 
311             // Check that the wearable has all the features.
312             boolean hasAllFeatures = true;
313             if (pkg.reqFeatures != null) {
314                 for (FeatureInfo feature : pkg.reqFeatures) {
315                     if (feature.name != null && !pm.hasSystemFeature(feature.name) &&
316                             (feature.flags & FeatureInfo.FLAG_REQUIRED) != 0) {
317                         Log.e(TAG, "Wearable does not have required feature: " + feature +
318                                 " for " + packageName);
319                         hasAllFeatures = false;
320                     }
321                 }
322             }
323 
324             if (!hasAllFeatures) {
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         PowerManager.WakeLock lock = getLock(this.getApplicationContext());
359         pm.deletePackage(packageName, new PackageDeleteObserver(lock, startId),
360                 PackageManager.DELETE_ALL_USERS);
361         startPermsServiceForUninstall(packageName);
362         Log.i(TAG, "Sent delete request for " + packageName);
363     }
364 
checkPermissions(PackageParser.Package pkg, int companionSdkVersion, int companionDeviceVersion, Uri permUri, List<String> wearablePermissions, File apkFile)365     private boolean checkPermissions(PackageParser.Package pkg, int companionSdkVersion,
366             int companionDeviceVersion, Uri permUri, List<String> wearablePermissions,
367             File apkFile) {
368         // If the Wear App is targeted for M-release, since the permission model has been changed,
369         // permissions may not be granted on the phone yet. We need a different flow for user to
370         // accept these permissions.
371         //
372         // Assumption: Code is running on E-release, so Wear is always running M.
373         // - Case 1: If the Wear App(WA) is targeting 23, always choose the M model (4 cases)
374         // - Case 2: Else if the Phone App(PA) is targeting 23 and Phone App(P) is running on M,
375         // show a Dialog so that the user can accept all perms (1 case)
376         //   - Also show a warning to the developer if the watch is targeting M
377         // - Case 3: If Case 2 is false, then the behavior on the phone is pre-M. Stick to pre-M
378         // behavior on watch (as long as we don't hit case 1).
379         //   - 3a: WA(22) PA(22) P(22) -> watch app is not targeting 23
380         //   - 3b: WA(22) PA(22) P(23) -> watch app is not targeting 23
381         //   - 3c: WA(22) PA(23) P(22) -> watch app is not targeting 23
382         // - Case 4: We did not get Companion App's/Device's version, always show dialog to user to
383         // accept permissions. (This happens if the AndroidWear Companion App is really old).
384         boolean isWearTargetingM =
385                 pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
386         if (isWearTargetingM) { // Case 1
387             // Install the app if Wear App is ready for the new perms model.
388             return true;
389         }
390 
391         List<String> unavailableWearablePerms = getWearPermsNotGrantedOnPhone(pkg.packageName,
392                 permUri, wearablePermissions);
393         if (unavailableWearablePerms == null) {
394             return false;
395         }
396 
397         if (unavailableWearablePerms.size() == 0) {
398             // All permissions requested by the watch are already granted on the phone, no need
399             // to do anything.
400             return true;
401         }
402 
403         // Cases 2 and 4.
404         boolean isCompanionTargetingM = companionSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
405         boolean isCompanionRunningM = companionDeviceVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
406         if (isCompanionTargetingM) { // Case 2 Warning
407             Log.w(TAG, "MNC: Wear app's targetSdkVersion should be at least 23, if " +
408                     "phone app is targeting at least 23, will continue.");
409         }
410         if ((isCompanionTargetingM && isCompanionRunningM) || // Case 2
411                 companionSdkVersion == 0 || companionDeviceVersion == 0) { // Case 4
412             startPermsServiceForInstall(pkg, apkFile, unavailableWearablePerms);
413         }
414 
415         // Case 3a-3c.
416         return false;
417     }
418 
419     /**
420      * Given a {@string packageName} corresponding to a phone app, query the provider for all the
421      * perms that are granted.
422      * @return null if there is an error retrieving this info
423      *         else, a list of all the wearable perms that are not in the list of granted perms of
424      * the phone.
425      */
getWearPermsNotGrantedOnPhone(String packageName, Uri permUri, List<String> wearablePermissions)426     private List<String> getWearPermsNotGrantedOnPhone(String packageName, Uri permUri,
427             List<String> wearablePermissions) {
428         if (permUri == null) {
429             Log.e(TAG, "Permission URI is null");
430             return null;
431         }
432         Cursor permCursor = getContentResolver().query(permUri, null, null, null, null);
433         if (permCursor == null) {
434             Log.e(TAG, "Could not get the cursor for the permissions");
435             return null;
436         }
437 
438         Set<String> grantedPerms = new HashSet<>();
439         Set<String> ungrantedPerms = new HashSet<>();
440         while(permCursor.moveToNext()) {
441             // Make sure that the MatrixCursor returned by the ContentProvider has 2 columns and
442             // verify their types.
443             if (permCursor.getColumnCount() == 2
444                     && Cursor.FIELD_TYPE_STRING == permCursor.getType(0)
445                     && Cursor.FIELD_TYPE_INTEGER == permCursor.getType(1)) {
446                 String perm = permCursor.getString(0);
447                 Integer granted = permCursor.getInt(1);
448                 if (granted == 1) {
449                     grantedPerms.add(perm);
450                 } else {
451                     ungrantedPerms.add(perm);
452                 }
453             }
454         }
455         permCursor.close();
456 
457         ArrayList<String> unavailableWearablePerms = new ArrayList<>();
458         for (String wearablePerm : wearablePermissions) {
459             if (!grantedPerms.contains(wearablePerm)) {
460                 unavailableWearablePerms.add(wearablePerm);
461                 if (!ungrantedPerms.contains(wearablePerm)) {
462                     // This is an error condition. This means that the wearable has permissions that
463                     // are not even declared in its host app. This is a developer error.
464                     Log.e(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm
465                             + "\" that is not defined in the host application's manifest.");
466                 } else {
467                     Log.w(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm +
468                             "\" that is not granted in the host application.");
469                 }
470             }
471         }
472         return unavailableWearablePerms;
473     }
474 
finishService(PowerManager.WakeLock lock, int startId)475     private void finishService(PowerManager.WakeLock lock, int startId) {
476         if (lock.isHeld()) {
477             lock.release();
478         }
479         stopSelf(startId);
480     }
481 
getLock(Context context)482     private synchronized PowerManager.WakeLock getLock(Context context) {
483         if (lockStatic == null) {
484             PowerManager mgr =
485                     (PowerManager) context.getSystemService(Context.POWER_SERVICE);
486             lockStatic = mgr.newWakeLock(
487                     PowerManager.PARTIAL_WAKE_LOCK, context.getClass().getSimpleName());
488             lockStatic.setReferenceCounted(true);
489         }
490         return lockStatic;
491     }
492 
startPermsServiceForInstall(final PackageParser.Package pkg, final File apkFile, List<String> unavailableWearablePerms)493     private void startPermsServiceForInstall(final PackageParser.Package pkg, final File apkFile,
494             List<String> unavailableWearablePerms) {
495         final String packageName = pkg.packageName;
496 
497         Intent showPermsIntent = new Intent()
498                 .setComponent(new ComponentName(HOME_APP_PACKAGE_NAME, SHOW_PERMS_SERVICE_CLASS))
499                 .setAction(Intent.ACTION_INSTALL_PACKAGE);
500         final PackageManager pm = getPackageManager();
501         pkg.applicationInfo.publicSourceDir = apkFile.getPath();
502         final CharSequence label = pkg.applicationInfo.loadLabel(pm);
503         final Uri iconUri = getIconFileUri(packageName, pkg.applicationInfo.loadIcon(pm));
504         if (TextUtils.isEmpty(label) || iconUri == null) {
505             Log.e(TAG, "MNC: Could not launch service since either label " + label +
506                     ", or icon Uri " + iconUri + " is invalid.");
507         } else {
508             showPermsIntent.putExtra(KEY_APP_LABEL, label);
509             showPermsIntent.putExtra(KEY_APP_ICON_URI, iconUri);
510             showPermsIntent.putExtra(KEY_PACKAGE_NAME, packageName);
511             showPermsIntent.putExtra(KEY_PERMS_LIST,
512                     unavailableWearablePerms.toArray(new String[0]));
513             showPermsIntent.putExtra(KEY_HAS_LAUNCHER, WearPackageUtil.hasLauncherActivity(pkg));
514 
515             if (Log.isLoggable(TAG, Log.DEBUG)) {
516                 Log.d(TAG, "MNC: Launching Intent " + showPermsIntent + " for " + packageName +
517                         " with name " + label);
518             }
519             startService(showPermsIntent);
520         }
521     }
522 
startPermsServiceForUninstall(final String packageName)523     private void startPermsServiceForUninstall(final String packageName) {
524         Intent showPermsIntent = new Intent()
525                 .setComponent(new ComponentName(HOME_APP_PACKAGE_NAME, SHOW_PERMS_SERVICE_CLASS))
526                 .setAction(Intent.ACTION_UNINSTALL_PACKAGE);
527         showPermsIntent.putExtra(KEY_PACKAGE_NAME, packageName);
528         if (Log.isLoggable(TAG, Log.DEBUG)) {
529             Log.d(TAG, "Launching Intent " + showPermsIntent + " for " + packageName);
530         }
531         startService(showPermsIntent);
532     }
533 
getIconFileUri(final String packageName, final Drawable d)534     private Uri getIconFileUri(final String packageName, final Drawable d) {
535         if (d == null || !(d instanceof BitmapDrawable)) {
536             Log.e(TAG, "Drawable is not a BitmapDrawable for " + packageName);
537             return null;
538         }
539         File iconFile = WearPackageUtil.getIconFile(this, packageName);
540 
541         if (iconFile == null) {
542             Log.e(TAG, "Could not get icon file for " + packageName);
543             return null;
544         }
545 
546         FileOutputStream fos = null;
547         try {
548             // Convert bitmap to byte array
549             Bitmap bitmap = ((BitmapDrawable) d).getBitmap();
550             ByteArrayOutputStream bos = new ByteArrayOutputStream();
551             bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
552 
553             // Write the bytes into the file
554             fos = new FileOutputStream(iconFile);
555             fos.write(bos.toByteArray());
556             fos.flush();
557 
558             return WearPackageIconProvider.getUriForPackage(packageName);
559         } catch (IOException e) {
560             Log.e(TAG, "Could not convert drawable to icon file for package " + packageName, e);
561             return null;
562         } finally {
563             if (fos != null) {
564                 try {
565                     fos.close();
566                 } catch (IOException e) {
567                     // ignore
568                 }
569             }
570         }
571     }
572 
573     private class PackageInstallListener implements PackageInstallerImpl.InstallListener {
574         private Context mContext;
575         private PowerManager.WakeLock mWakeLock;
576         private int mStartId;
577         private String mApplicationPackageName;
PackageInstallListener(Context context, PowerManager.WakeLock wakeLock, int startId, String applicationPackageName)578         private PackageInstallListener(Context context, PowerManager.WakeLock wakeLock,
579                 int startId, String applicationPackageName) {
580             mContext = context;
581             mWakeLock = wakeLock;
582             mStartId = startId;
583             mApplicationPackageName = applicationPackageName;
584         }
585 
586         @Override
installBeginning()587         public void installBeginning() {
588             Log.i(TAG, "Package " + mApplicationPackageName + " is being installed.");
589         }
590 
591         @Override
installSucceeded()592         public void installSucceeded() {
593             try {
594                 Log.i(TAG, "Package " + mApplicationPackageName + " was installed.");
595 
596                 // Delete tempFile from the file system.
597                 File tempFile = WearPackageUtil.getTemporaryFile(mContext, mApplicationPackageName);
598                 if (tempFile != null) {
599                     tempFile.delete();
600                 }
601             } finally {
602                 finishService(mWakeLock, mStartId);
603             }
604         }
605 
606         @Override
installFailed(int errorCode, String errorDesc)607         public void installFailed(int errorCode, String errorDesc) {
608             Log.e(TAG, "Package install failed " + mApplicationPackageName
609                     + ", errorCode " + errorCode);
610             WearPackageUtil.removeFromPermStore(mContext, mApplicationPackageName);
611             finishService(mWakeLock, mStartId);
612         }
613     }
614 
615     private class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
616         private PowerManager.WakeLock mWakeLock;
617         private int mStartId;
618 
PackageDeleteObserver(PowerManager.WakeLock wakeLock, int startId)619         private PackageDeleteObserver(PowerManager.WakeLock wakeLock, int startId) {
620             mWakeLock = wakeLock;
621             mStartId = startId;
622         }
623 
packageDeleted(String packageName, int returnCode)624         public void packageDeleted(String packageName, int returnCode) {
625             try {
626                 if (returnCode >= 0) {
627                     Log.i(TAG, "Package " + packageName + " was uninstalled.");
628                 } else {
629                     Log.e(TAG, "Package uninstall failed " + packageName + ", returnCode " +
630                             returnCode);
631                 }
632             } finally {
633                 finishService(mWakeLock, mStartId);
634             }
635         }
636     }
637 }
638