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.Notification;
20 import android.app.NotificationChannel;
21 import android.app.NotificationManager;
22 import android.app.PendingIntent;
23 import android.app.Service;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.FeatureInfo;
28 import android.content.pm.PackageInfo;
29 import android.content.pm.PackageInstaller;
30 import android.content.pm.PackageManager;
31 import android.content.pm.VersionedPackage;
32 import android.database.Cursor;
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.util.ArrayMap;
45 import android.util.Log;
46 import android.util.Pair;
47 
48 import androidx.annotation.Nullable;
49 
50 import com.android.packageinstaller.DeviceUtils;
51 import com.android.packageinstaller.EventResultPersister;
52 import com.android.packageinstaller.PackageUtil;
53 import com.android.packageinstaller.R;
54 import com.android.packageinstaller.UninstallEventReceiver;
55 
56 import java.io.File;
57 import java.io.FileNotFoundException;
58 import java.util.Arrays;
59 import java.util.HashMap;
60 import java.util.HashSet;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Set;
64 
65 /**
66  * Service that will install/uninstall packages. It will check for permissions and features as well.
67  *
68  * -----------
69  *
70  * Debugging information:
71  *
72  *  Install Action example:
73  *  adb shell am startservice -a com.android.packageinstaller.wear.INSTALL_PACKAGE \
74  *     -d package://com.google.android.gms \
75  *     --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 \
76  *     --es android.intent.extra.INSTALLER_PACKAGE_NAME com.google.android.gms \
77  *     --ez com.google.android.clockwork.EXTRA_CHECK_PERMS false \
78  *     --eu com.google.android.clockwork.EXTRA_PERM_URI content://com.google.android.clockwork.home.provider/host/com.google.android.wearable.app/permissions \
79  *     com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
80  *
81  *  Uninstall Action example:
82  *  adb shell am startservice -a com.android.packageinstaller.wear.UNINSTALL_PACKAGE \
83  *     -d package://com.google.android.gms \
84  *     com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
85  *
86  *  Retry GMS:
87  *  adb shell am startservice -a com.android.packageinstaller.wear.RETRY_GMS \
88  *     com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
89  */
90 public class WearPackageInstallerService extends Service
91         implements EventResultPersister.EventResultObserver {
92     private static final String TAG = "WearPkgInstallerService";
93 
94     private static final String WEAR_APPS_CHANNEL = "wear_app_install_uninstall";
95     private static final String BROADCAST_ACTION =
96             "com.android.packageinstaller.ACTION_UNINSTALL_COMMIT";
97 
98     private final int START_INSTALL = 1;
99     private final int START_UNINSTALL = 2;
100 
101     private int mInstallNotificationId = 1;
102     private final Map<String, Integer> mNotifIdMap = new ArrayMap<>();
103     private final Map<Integer, UninstallParams> mServiceIdToParams = new HashMap<>();
104 
105     private class UninstallParams {
106         public String mPackageName;
107         public PowerManager.WakeLock mLock;
108 
UninstallParams(String packageName, PowerManager.WakeLock lock)109         UninstallParams(String packageName, PowerManager.WakeLock lock) {
110             mPackageName = packageName;
111             mLock = lock;
112         }
113     }
114 
115     private final class ServiceHandler extends Handler {
ServiceHandler(Looper looper)116         public ServiceHandler(Looper looper) {
117             super(looper);
118         }
119 
handleMessage(Message msg)120         public void handleMessage(Message msg) {
121             switch (msg.what) {
122                 case START_INSTALL:
123                     installPackage(msg.getData());
124                     break;
125                 case START_UNINSTALL:
126                     uninstallPackage(msg.getData());
127                     break;
128             }
129         }
130     }
131     private ServiceHandler mServiceHandler;
132     private NotificationChannel mNotificationChannel;
133     private static volatile PowerManager.WakeLock lockStatic = null;
134 
135     @Override
onBind(Intent intent)136     public IBinder onBind(Intent intent) {
137         return null;
138     }
139 
140     @Override
onCreate()141     public void onCreate() {
142         super.onCreate();
143         HandlerThread thread = new HandlerThread("PackageInstallerThread",
144                 Process.THREAD_PRIORITY_BACKGROUND);
145         thread.start();
146 
147         mServiceHandler = new ServiceHandler(thread.getLooper());
148     }
149 
150     @Override
onStartCommand(Intent intent, int flags, int startId)151     public int onStartCommand(Intent intent, int flags, int startId) {
152         if (!DeviceUtils.isWear(this)) {
153             Log.w(TAG, "Not running on wearable.");
154             finishServiceEarly(startId);
155             return START_NOT_STICKY;
156         }
157 
158         if (intent == null) {
159             Log.w(TAG, "Got null intent.");
160             finishServiceEarly(startId);
161             return START_NOT_STICKY;
162         }
163 
164         if (Log.isLoggable(TAG, Log.DEBUG)) {
165             Log.d(TAG, "Got install/uninstall request " + intent);
166         }
167 
168         Uri packageUri = intent.getData();
169         if (packageUri == null) {
170             Log.e(TAG, "No package URI in intent");
171             finishServiceEarly(startId);
172             return START_NOT_STICKY;
173         }
174 
175         final String packageName = WearPackageUtil.getSanitizedPackageName(packageUri);
176         if (packageName == null) {
177             Log.e(TAG, "Invalid package name in URI (expected package:<pkgName>): " + packageUri);
178             finishServiceEarly(startId);
179             return START_NOT_STICKY;
180         }
181 
182         PowerManager.WakeLock lock = getLock(this.getApplicationContext());
183         if (!lock.isHeld()) {
184             lock.acquire();
185         }
186 
187         Bundle intentBundle = intent.getExtras();
188         if (intentBundle == null) {
189             intentBundle = new Bundle();
190         }
191         WearPackageArgs.setStartId(intentBundle, startId);
192         WearPackageArgs.setPackageName(intentBundle, packageName);
193         Message msg;
194         String notifTitle;
195         if (Intent.ACTION_INSTALL_PACKAGE.equals(intent.getAction())) {
196             msg = mServiceHandler.obtainMessage(START_INSTALL);
197             notifTitle = getString(R.string.installing);
198         } else if (Intent.ACTION_UNINSTALL_PACKAGE.equals(intent.getAction())) {
199             msg = mServiceHandler.obtainMessage(START_UNINSTALL);
200             notifTitle = getString(R.string.uninstalling);
201         } else {
202             Log.e(TAG, "Unknown action : " + intent.getAction());
203             finishServiceEarly(startId);
204             return START_NOT_STICKY;
205         }
206         Pair<Integer, Notification> notifPair = buildNotification(packageName, notifTitle);
207         startForeground(notifPair.first, notifPair.second);
208         msg.setData(intentBundle);
209         mServiceHandler.sendMessage(msg);
210         return START_NOT_STICKY;
211     }
212 
installPackage(Bundle argsBundle)213     private void installPackage(Bundle argsBundle) {
214         int startId = WearPackageArgs.getStartId(argsBundle);
215         final String packageName = WearPackageArgs.getPackageName(argsBundle);
216         final Uri assetUri = WearPackageArgs.getAssetUri(argsBundle);
217         final Uri permUri = WearPackageArgs.getPermUri(argsBundle);
218         boolean checkPerms = WearPackageArgs.checkPerms(argsBundle);
219         boolean skipIfSameVersion = WearPackageArgs.skipIfSameVersion(argsBundle);
220         int companionSdkVersion = WearPackageArgs.getCompanionSdkVersion(argsBundle);
221         int companionDeviceVersion = WearPackageArgs.getCompanionDeviceVersion(argsBundle);
222         String compressionAlg = WearPackageArgs.getCompressionAlg(argsBundle);
223         boolean skipIfLowerVersion = WearPackageArgs.skipIfLowerVersion(argsBundle);
224 
225         if (Log.isLoggable(TAG, Log.DEBUG)) {
226             Log.d(TAG, "Installing package: " + packageName + ", assetUri: " + assetUri +
227                     ",permUri: " + permUri + ", startId: " + startId + ", checkPerms: " +
228                     checkPerms + ", skipIfSameVersion: " + skipIfSameVersion +
229                     ", compressionAlg: " + compressionAlg + ", companionSdkVersion: " +
230                     companionSdkVersion + ", companionDeviceVersion: " + companionDeviceVersion +
231                     ", skipIfLowerVersion: " + skipIfLowerVersion);
232         }
233         final PackageManager pm = getPackageManager();
234         File tempFile = null;
235         PowerManager.WakeLock lock = getLock(this.getApplicationContext());
236         boolean messageSent = false;
237         try {
238             PackageInfo existingPkgInfo = null;
239             try {
240                 existingPkgInfo = pm.getPackageInfo(packageName,
241                         PackageManager.MATCH_ANY_USER | PackageManager.GET_PERMISSIONS);
242                 if (existingPkgInfo != null) {
243                     if (Log.isLoggable(TAG, Log.DEBUG)) {
244                         Log.d(TAG, "Replacing package:" + packageName);
245                     }
246                 }
247             } catch (PackageManager.NameNotFoundException e) {
248                 // Ignore this exception. We could not find the package, will treat as a new
249                 // installation.
250             }
251             // TODO(28021618): This was left as a temp file due to the fact that this code is being
252             //       deprecated and that we need the bare minimum to continue working moving forward
253             //       If this code is used as reference, this permission logic might want to be
254             //       reworked to use a stream instead of a file so that we don't need to write a
255             //       file at all.  Note that there might be some trickiness with opening a stream
256             //       for multiple users.
257             ParcelFileDescriptor parcelFd = getContentResolver()
258                     .openFileDescriptor(assetUri, "r");
259             tempFile = WearPackageUtil.getFileFromFd(WearPackageInstallerService.this,
260                     parcelFd, packageName, compressionAlg);
261             if (tempFile == null) {
262                 Log.e(TAG, "Could not create a temp file from FD for " + packageName);
263                 return;
264             }
265             PackageInfo pkgInfo = PackageUtil.getPackageInfo(this, tempFile,
266                     PackageManager.GET_PERMISSIONS | PackageManager.GET_CONFIGURATIONS);
267             if (pkgInfo == null) {
268                 Log.e(TAG, "Could not parse apk information for " + packageName);
269                 return;
270             }
271 
272             if (!pkgInfo.packageName.equals(packageName)) {
273                 Log.e(TAG, "Wearable Package Name has to match what is provided for " +
274                         packageName);
275                 return;
276             }
277 
278             ApplicationInfo appInfo = pkgInfo.applicationInfo;
279             appInfo.sourceDir = tempFile.getPath();
280             appInfo.publicSourceDir = tempFile.getPath();
281             getLabelAndUpdateNotification(packageName,
282                     getString(R.string.installing_app, appInfo.loadLabel(pm)));
283 
284             List<String> wearablePerms = Arrays.asList(pkgInfo.requestedPermissions);
285 
286             // Log if the installed pkg has a higher version number.
287             if (existingPkgInfo != null) {
288                 long longVersionCode = pkgInfo.getLongVersionCode();
289                 if (existingPkgInfo.getLongVersionCode() == longVersionCode) {
290                     if (skipIfSameVersion) {
291                         Log.w(TAG, "Version number (" + longVersionCode +
292                                 ") of new app is equal to existing app for " + packageName +
293                                 "; not installing due to versionCheck");
294                         return;
295                     } else {
296                         Log.w(TAG, "Version number of new app (" + longVersionCode +
297                                 ") is equal to existing app for " + packageName);
298                     }
299                 } else if (existingPkgInfo.getLongVersionCode() > longVersionCode) {
300                     if (skipIfLowerVersion) {
301                         // Starting in Feldspar, we are not going to allow downgrades of any app.
302                         Log.w(TAG, "Version number of new app (" + longVersionCode +
303                                 ") is lower than existing app ( "
304                                 + existingPkgInfo.getLongVersionCode() +
305                                 ") for " + packageName + "; not installing due to versionCheck");
306                         return;
307                     } else {
308                         Log.w(TAG, "Version number of new app (" + longVersionCode +
309                                 ") is lower than existing app ( "
310                                 + existingPkgInfo.getLongVersionCode() + ") for " + packageName);
311                     }
312                 }
313 
314                 // Following the Android Phone model, we should only check for permissions for any
315                 // newly defined perms.
316                 if (existingPkgInfo.requestedPermissions != null) {
317                     for (int i = 0; i < existingPkgInfo.requestedPermissions.length; ++i) {
318                         // If the permission is granted, then we will not ask to request it again.
319                         if ((existingPkgInfo.requestedPermissionsFlags[i] &
320                                 PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
321                             if (Log.isLoggable(TAG, Log.DEBUG)) {
322                                 Log.d(TAG, existingPkgInfo.requestedPermissions[i] +
323                                         " is already granted for " + packageName);
324                             }
325                             wearablePerms.remove(existingPkgInfo.requestedPermissions[i]);
326                         }
327                     }
328                 }
329             }
330 
331             // Check that the wearable has all the features.
332             boolean hasAllFeatures = true;
333             for (FeatureInfo feature : pkgInfo.reqFeatures) {
334                 if (feature.name != null && !pm.hasSystemFeature(feature.name) &&
335                         (feature.flags & FeatureInfo.FLAG_REQUIRED) != 0) {
336                     Log.e(TAG, "Wearable does not have required feature: " + feature +
337                             " for " + packageName);
338                     hasAllFeatures = false;
339                 }
340             }
341 
342             if (!hasAllFeatures) {
343                 return;
344             }
345 
346             // Check permissions on both the new wearable package and also on the already installed
347             // wearable package.
348             // If the app is targeting API level 23, we will also start a service in ClockworkHome
349             // which will ultimately prompt the user to accept/reject permissions.
350             if (checkPerms && !checkPermissions(pkgInfo, companionSdkVersion,
351                     companionDeviceVersion, permUri, wearablePerms, tempFile)) {
352                 Log.w(TAG, "Wearable does not have enough permissions.");
353                 return;
354             }
355 
356             // Finally install the package.
357             ParcelFileDescriptor fd = getContentResolver().openFileDescriptor(assetUri, "r");
358             PackageInstallerFactory.getPackageInstaller(this).install(packageName, fd,
359                     new PackageInstallListener(this, lock, startId, packageName));
360 
361             messageSent = true;
362             Log.i(TAG, "Sent installation request for " + packageName);
363         } catch (FileNotFoundException e) {
364             Log.e(TAG, "Could not find the file with URI " + assetUri, e);
365         } finally {
366             if (!messageSent) {
367                 // Some error happened. If the message has been sent, we can wait for the observer
368                 // which will finish the service.
369                 if (tempFile != null) {
370                     tempFile.delete();
371                 }
372                 finishService(lock, startId);
373             }
374         }
375     }
376 
377     // TODO: This was left using the old PackageManager API due to the fact that this code is being
378     //       deprecated and that we need the bare minimum to continue working moving forward
379     //       If this code is used as reference, this logic should be reworked to use the new
380     //       PackageInstaller APIs similar to how installPackage was reworked
uninstallPackage(Bundle argsBundle)381     private void uninstallPackage(Bundle argsBundle) {
382         int startId = WearPackageArgs.getStartId(argsBundle);
383         final String packageName = WearPackageArgs.getPackageName(argsBundle);
384 
385         PowerManager.WakeLock lock = getLock(this.getApplicationContext());
386 
387         UninstallParams params = new UninstallParams(packageName, lock);
388         mServiceIdToParams.put(startId, params);
389 
390         final PackageManager pm = getPackageManager();
391         try {
392             PackageInfo pkgInfo = pm.getPackageInfo(packageName, 0);
393             getLabelAndUpdateNotification(packageName,
394                     getString(R.string.uninstalling_app, pkgInfo.applicationInfo.loadLabel(pm)));
395 
396             int uninstallId = UninstallEventReceiver.addObserver(this,
397                     EventResultPersister.GENERATE_NEW_ID, this);
398 
399             Intent broadcastIntent = new Intent(BROADCAST_ACTION);
400             broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
401             broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, uninstallId);
402             broadcastIntent.putExtra(EventResultPersister.EXTRA_SERVICE_ID, startId);
403             broadcastIntent.setPackage(getPackageName());
404 
405             PendingIntent pendingIntent = PendingIntent.getBroadcast(this, uninstallId,
406                     broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT
407                             | PendingIntent.FLAG_MUTABLE);
408 
409             // Found package, send uninstall request.
410             pm.getPackageInstaller().uninstall(
411                     new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
412                     PackageManager.DELETE_ALL_USERS,
413                     pendingIntent.getIntentSender());
414 
415             Log.i(TAG, "Sent delete request for " + packageName);
416         } catch (IllegalArgumentException | PackageManager.NameNotFoundException e) {
417             // Couldn't find the package, no need to call uninstall.
418             Log.w(TAG, "Could not find package, not deleting " + packageName, e);
419             finishService(lock, startId);
420         } catch (EventResultPersister.OutOfIdsException e) {
421             Log.e(TAG, "Fails to start uninstall", e);
422             finishService(lock, startId);
423         }
424     }
425 
426     @Override
onResult(int status, int legacyStatus, @Nullable String message, int serviceId)427     public void onResult(int status, int legacyStatus, @Nullable String message, int serviceId) {
428         if (mServiceIdToParams.containsKey(serviceId)) {
429             UninstallParams params = mServiceIdToParams.get(serviceId);
430             try {
431                 if (status == PackageInstaller.STATUS_SUCCESS) {
432                     Log.i(TAG, "Package " + params.mPackageName + " was uninstalled.");
433                 } else {
434                     Log.e(TAG, "Package uninstall failed " + params.mPackageName
435                             + ", returnCode " + legacyStatus);
436                 }
437             } finally {
438                 finishService(params.mLock, serviceId);
439             }
440         }
441     }
442 
checkPermissions(PackageInfo pkgInfo, int companionSdkVersion, int companionDeviceVersion, Uri permUri, List<String> wearablePermissions, File apkFile)443     private boolean checkPermissions(PackageInfo pkgInfo, int companionSdkVersion,
444             int companionDeviceVersion, Uri permUri, List<String> wearablePermissions,
445             File apkFile) {
446         // Assumption: We are running on Android O.
447         // If the Phone App is targeting M, all permissions may not have been granted to the phone
448         // app. If the Wear App is then not targeting M, there may be permissions that are not
449         // granted on the Phone app (by the user) right now and we cannot just grant it for the Wear
450         // app.
451         if (pkgInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
452             // Install the app if Wear App is ready for the new perms model.
453             return true;
454         }
455 
456         if (!doesWearHaveUngrantedPerms(pkgInfo.packageName, permUri, wearablePermissions)) {
457             // All permissions requested by the watch are already granted on the phone, no need
458             // to do anything.
459             return true;
460         }
461 
462         // Log an error if Wear is targeting < 23 and phone is targeting >= 23.
463         if (companionSdkVersion == 0 || companionSdkVersion >= Build.VERSION_CODES.M) {
464             Log.e(TAG, "MNC: Wear app's targetSdkVersion should be at least 23, if "
465                     + "phone app is targeting at least 23, will continue.");
466         }
467 
468         return false;
469     }
470 
471     /**
472      * Given a {@string packageName} corresponding to a phone app, query the provider for all the
473      * perms that are granted.
474      *
475      * @return true if the Wear App has any perms that have not been granted yet on the phone side.
476      * @return true if there is any error cases.
477      */
doesWearHaveUngrantedPerms(String packageName, Uri permUri, List<String> wearablePermissions)478     private boolean doesWearHaveUngrantedPerms(String packageName, Uri permUri,
479             List<String> wearablePermissions) {
480         if (permUri == null) {
481             Log.e(TAG, "Permission URI is null");
482             // Pretend there is an ungranted permission to avoid installing for error cases.
483             return true;
484         }
485         Cursor permCursor = getContentResolver().query(permUri, null, null, null, null);
486         if (permCursor == null) {
487             Log.e(TAG, "Could not get the cursor for the permissions");
488             // Pretend there is an ungranted permission to avoid installing for error cases.
489             return true;
490         }
491 
492         Set<String> grantedPerms = new HashSet<>();
493         Set<String> ungrantedPerms = new HashSet<>();
494         while(permCursor.moveToNext()) {
495             // Make sure that the MatrixCursor returned by the ContentProvider has 2 columns and
496             // verify their types.
497             if (permCursor.getColumnCount() == 2
498                     && Cursor.FIELD_TYPE_STRING == permCursor.getType(0)
499                     && Cursor.FIELD_TYPE_INTEGER == permCursor.getType(1)) {
500                 String perm = permCursor.getString(0);
501                 Integer granted = permCursor.getInt(1);
502                 if (granted == 1) {
503                     grantedPerms.add(perm);
504                 } else {
505                     ungrantedPerms.add(perm);
506                 }
507             }
508         }
509         permCursor.close();
510 
511         boolean hasUngrantedPerm = false;
512         for (String wearablePerm : wearablePermissions) {
513             if (!grantedPerms.contains(wearablePerm)) {
514                 hasUngrantedPerm = true;
515                 if (!ungrantedPerms.contains(wearablePerm)) {
516                     // This is an error condition. This means that the wearable has permissions that
517                     // are not even declared in its host app. This is a developer error.
518                     Log.e(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm
519                             + "\" that is not defined in the host application's manifest.");
520                 } else {
521                     Log.w(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm +
522                             "\" that is not granted in the host application.");
523                 }
524             }
525         }
526         return hasUngrantedPerm;
527     }
528 
529     /** Finishes the service after fulfilling obligation to call startForeground. */
finishServiceEarly(int startId)530     private void finishServiceEarly(int startId) {
531         Pair<Integer, Notification> notifPair = buildNotification(
532                 getApplicationContext().getPackageName(), "");
533         startForeground(notifPair.first, notifPair.second);
534         finishService(null, startId);
535     }
536 
finishService(PowerManager.WakeLock lock, int startId)537     private void finishService(PowerManager.WakeLock lock, int startId) {
538         if (lock != null && lock.isHeld()) {
539             lock.release();
540         }
541         stopSelf(startId);
542     }
543 
getLock(Context context)544     private synchronized PowerManager.WakeLock getLock(Context context) {
545         if (lockStatic == null) {
546             PowerManager mgr =
547                     (PowerManager) context.getSystemService(Context.POWER_SERVICE);
548             lockStatic = mgr.newWakeLock(
549                     PowerManager.PARTIAL_WAKE_LOCK, context.getClass().getSimpleName());
550             lockStatic.setReferenceCounted(true);
551         }
552         return lockStatic;
553     }
554 
555     private class PackageInstallListener implements PackageInstallerImpl.InstallListener {
556         private Context mContext;
557         private PowerManager.WakeLock mWakeLock;
558         private int mStartId;
559         private String mApplicationPackageName;
PackageInstallListener(Context context, PowerManager.WakeLock wakeLock, int startId, String applicationPackageName)560         private PackageInstallListener(Context context, PowerManager.WakeLock wakeLock,
561                 int startId, String applicationPackageName) {
562             mContext = context;
563             mWakeLock = wakeLock;
564             mStartId = startId;
565             mApplicationPackageName = applicationPackageName;
566         }
567 
568         @Override
installBeginning()569         public void installBeginning() {
570             Log.i(TAG, "Package " + mApplicationPackageName + " is being installed.");
571         }
572 
573         @Override
installSucceeded()574         public void installSucceeded() {
575             try {
576                 Log.i(TAG, "Package " + mApplicationPackageName + " was installed.");
577 
578                 // Delete tempFile from the file system.
579                 File tempFile = WearPackageUtil.getTemporaryFile(mContext, mApplicationPackageName);
580                 if (tempFile != null) {
581                     tempFile.delete();
582                 }
583             } finally {
584                 finishService(mWakeLock, mStartId);
585             }
586         }
587 
588         @Override
installFailed(int errorCode, String errorDesc)589         public void installFailed(int errorCode, String errorDesc) {
590             Log.e(TAG, "Package install failed " + mApplicationPackageName
591                     + ", errorCode " + errorCode);
592             finishService(mWakeLock, mStartId);
593         }
594     }
595 
buildNotification(final String packageName, final String title)596     private synchronized Pair<Integer, Notification> buildNotification(final String packageName,
597             final String title) {
598         int notifId;
599         if (mNotifIdMap.containsKey(packageName)) {
600             notifId = mNotifIdMap.get(packageName);
601         } else {
602             notifId = mInstallNotificationId++;
603             mNotifIdMap.put(packageName, notifId);
604         }
605 
606         if (mNotificationChannel == null) {
607             mNotificationChannel = new NotificationChannel(WEAR_APPS_CHANNEL,
608                     getString(R.string.wear_app_channel), NotificationManager.IMPORTANCE_MIN);
609             NotificationManager notificationManager = getSystemService(NotificationManager.class);
610             notificationManager.createNotificationChannel(mNotificationChannel);
611         }
612         return new Pair<>(notifId, new Notification.Builder(this, WEAR_APPS_CHANNEL)
613             .setSmallIcon(R.drawable.ic_file_download)
614             .setContentTitle(title)
615             .build());
616     }
617 
getLabelAndUpdateNotification(String packageName, String title)618     private void getLabelAndUpdateNotification(String packageName, String title) {
619         // Update notification since we have a label now.
620         NotificationManager notificationManager = getSystemService(NotificationManager.class);
621         Pair<Integer, Notification> notifPair = buildNotification(packageName, title);
622         notificationManager.notify(notifPair.first, notifPair.second);
623     }
624 }
625