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