1 /*
2  * Copyright (C) 2017 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.server.backup.utils;
18 
19 import static com.android.server.backup.BackupManagerService.DEBUG;
20 import static com.android.server.backup.BackupManagerService.TAG;
21 
22 import android.content.Context;
23 import android.content.IIntentReceiver;
24 import android.content.IIntentSender;
25 import android.content.Intent;
26 import android.content.IntentSender;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageInfo;
29 import android.content.pm.PackageInstaller;
30 import android.content.pm.PackageInstaller.Session;
31 import android.content.pm.PackageInstaller.SessionParams;
32 import android.content.pm.PackageManager;
33 import android.content.pm.PackageManagerInternal;
34 import android.content.pm.Signature;
35 import android.os.Bundle;
36 import android.os.IBinder;
37 import android.os.UserHandle;
38 import android.util.Slog;
39 
40 import com.android.internal.annotations.GuardedBy;
41 import com.android.server.LocalServices;
42 import com.android.server.backup.FileMetadata;
43 import com.android.server.backup.restore.RestoreDeleteObserver;
44 import com.android.server.backup.restore.RestorePolicy;
45 
46 import java.io.IOException;
47 import java.io.InputStream;
48 import java.io.OutputStream;
49 import java.util.HashMap;
50 
51 /**
52  * Utility methods used by {@link com.android.server.backup.restore.PerformAdbRestoreTask} and
53  * {@link com.android.server.backup.restore.FullRestoreEngine}.
54  */
55 public class RestoreUtils {
56     /**
57      * Reads apk contents from input stream and installs the apk.
58      *
59      * @param instream - input stream to read apk data from.
60      * @param context - installing context
61      * @param deleteObserver - {@link RestoreDeleteObserver} instance.
62      * @param manifestSignatures - manifest signatures.
63      * @param packagePolicies - package policies.
64      * @param info - backup file info.
65      * @param installerPackageName - package name of installer.
66      * @param bytesReadListener - listener to be called for counting bytes read.
67      * @return true if apk was successfully read and installed and false otherwise.
68      */
69     // TODO: Refactor to get rid of unneeded params.
installApk(InputStream instream, Context context, RestoreDeleteObserver deleteObserver, HashMap<String, Signature[]> manifestSignatures, HashMap<String, RestorePolicy> packagePolicies, FileMetadata info, String installerPackageName, BytesReadListener bytesReadListener, int userId)70     public static boolean installApk(InputStream instream, Context context,
71             RestoreDeleteObserver deleteObserver,
72             HashMap<String, Signature[]> manifestSignatures,
73             HashMap<String, RestorePolicy> packagePolicies,
74             FileMetadata info,
75             String installerPackageName,
76             BytesReadListener bytesReadListener,
77             int userId) {
78         boolean okay = true;
79 
80         if (DEBUG) {
81             Slog.d(TAG, "Installing from backup: " + info.packageName);
82         }
83 
84         try {
85             LocalIntentReceiver receiver = new LocalIntentReceiver();
86             PackageManager packageManager = context.getPackageManager();
87             PackageInstaller installer = packageManager.getPackageInstaller();
88 
89             SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
90             params.setInstallerPackageName(installerPackageName);
91             int sessionId = installer.createSession(params);
92             try {
93                 try (Session session = installer.openSession(sessionId)) {
94                     try (OutputStream apkStream = session.openWrite(info.packageName, 0,
95                             info.size)) {
96                         byte[] buffer = new byte[32 * 1024];
97                         long size = info.size;
98                         while (size > 0) {
99                             long toRead = (buffer.length < size) ? buffer.length : size;
100                             int didRead = instream.read(buffer, 0, (int) toRead);
101                             if (didRead >= 0) {
102                                 bytesReadListener.onBytesRead(didRead);
103                             }
104                             apkStream.write(buffer, 0, didRead);
105                             size -= didRead;
106                         }
107                     }
108 
109                     // Installation is current disabled
110                     session.abandon();
111                     // session.commit(receiver.getIntentSender());
112                 }
113             } catch (Exception t) {
114                 installer.abandonSession(sessionId);
115 
116                 throw t;
117             }
118 
119             // Installation is current disabled
120             Intent result = null;
121             // Intent result = receiver.getResult();
122 
123             // Installation is current disabled
124             int status = PackageInstaller.STATUS_FAILURE;
125             // int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
126             //        PackageInstaller.STATUS_FAILURE);
127 
128             if (status != PackageInstaller.STATUS_SUCCESS) {
129                 // The only time we continue to accept install of data even if the
130                 // apk install failed is if we had already determined that we could
131                 // accept the data regardless.
132                 if (packagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
133                     okay = false;
134                 }
135             } else {
136                 // Okay, the install succeeded.  Make sure it was the right app.
137                 boolean uninstall = false;
138                 final String installedPackageName = result.getStringExtra(
139                         PackageInstaller.EXTRA_PACKAGE_NAME);
140                 if (!installedPackageName.equals(info.packageName)) {
141                     Slog.w(TAG, "Restore stream claimed to include apk for "
142                             + info.packageName + " but apk was really "
143                             + installedPackageName);
144                     // delete the package we just put in place; it might be fraudulent
145                     okay = false;
146                     uninstall = true;
147                 } else {
148                     try {
149                         PackageInfo pkg = packageManager.getPackageInfoAsUser(info.packageName,
150                                 PackageManager.GET_SIGNING_CERTIFICATES, userId);
151                         if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP)
152                                 == 0) {
153                             Slog.w(TAG, "Restore stream contains apk of package "
154                                     + info.packageName
155                                     + " but it disallows backup/restore");
156                             okay = false;
157                         } else {
158                             // So far so good -- do the signatures match the manifest?
159                             Signature[] sigs = manifestSignatures.get(info.packageName);
160                             PackageManagerInternal pmi = LocalServices.getService(
161                                     PackageManagerInternal.class);
162                             BackupEligibilityRules eligibilityRules =
163                                     BackupEligibilityRules.forBackup(packageManager, pmi, userId,
164                                             context);
165                             if (eligibilityRules.signaturesMatch(sigs, pkg)) {
166                                 // If this is a system-uid app without a declared backup agent,
167                                 // don't restore any of the file data.
168                                 if (UserHandle.isCore(pkg.applicationInfo.uid)
169                                         && (pkg.applicationInfo.backupAgentName == null)) {
170                                     Slog.w(TAG, "Installed app " + info.packageName
171                                             + " has restricted uid and no agent");
172                                     okay = false;
173                                 }
174                             } else {
175                                 Slog.w(TAG, "Installed app " + info.packageName
176                                         + " signatures do not match restore manifest");
177                                 okay = false;
178                                 uninstall = true;
179                             }
180                         }
181                     } catch (PackageManager.NameNotFoundException e) {
182                         Slog.w(TAG, "Install of package " + info.packageName
183                                 + " succeeded but now not found");
184                         okay = false;
185                     }
186                 }
187 
188                 // If we're not okay at this point, we need to delete the package
189                 // that we just installed.
190                 if (uninstall) {
191                     deleteObserver.reset();
192                     packageManager.deletePackage(
193                             installedPackageName,
194                             deleteObserver, 0);
195                     deleteObserver.waitForCompletion();
196                 }
197             }
198         } catch (IOException e) {
199             Slog.e(TAG, "Unable to transcribe restored apk for install");
200             okay = false;
201         }
202 
203         return okay;
204     }
205 
206     private static class LocalIntentReceiver {
207         private final Object mLock = new Object();
208 
209         @GuardedBy("mLock")
210         private Intent mResult = null;
211 
212         private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
213             @Override
214             public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
215                     IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
216                 synchronized (mLock) {
217                     mResult = intent;
218                     mLock.notifyAll();
219                 }
220             }
221         };
222 
getIntentSender()223         public IntentSender getIntentSender() {
224             return new IntentSender((IIntentSender) mLocalSender);
225         }
226 
getResult()227         public Intent getResult() {
228             synchronized (mLock) {
229                 while (mResult == null) {
230                     try {
231                         mLock.wait();
232                     } catch (InterruptedException e) {
233                         // ignored
234                     }
235                 }
236 
237                 return mResult;
238             }
239         }
240     }
241 }
242