OSDN Git Service

am 40190375: am 6fe2184b: Do not treat the dummy animation as an animation
[android-x86/frameworks-base.git] / services / core / java / com / android / server / pm / PackageInstallerSession.java
1 /*
2  * Copyright (C) 2014 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.pm;
18
19 import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
20 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
21 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
22 import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
23 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
24
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.IPackageInstallObserver2;
27 import android.content.pm.IPackageInstallerSession;
28 import android.content.pm.PackageInstallerParams;
29 import android.content.pm.PackageManager;
30 import android.content.pm.PackageParser;
31 import android.content.pm.PackageParser.PackageLite;
32 import android.content.pm.Signature;
33 import android.os.Build;
34 import android.os.Bundle;
35 import android.os.FileBridge;
36 import android.os.FileUtils;
37 import android.os.Handler;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.os.ParcelFileDescriptor;
41 import android.os.RemoteException;
42 import android.os.SELinux;
43 import android.system.ErrnoException;
44 import android.system.OsConstants;
45 import android.system.StructStat;
46 import android.util.ArraySet;
47 import android.util.Slog;
48
49 import com.android.internal.content.NativeLibraryHelper;
50 import com.android.internal.util.ArrayUtils;
51 import com.android.internal.util.Preconditions;
52
53 import libcore.io.IoUtils;
54 import libcore.io.Libcore;
55 import libcore.io.Streams;
56
57 import java.io.File;
58 import java.io.FileDescriptor;
59 import java.io.FileInputStream;
60 import java.io.FileOutputStream;
61 import java.io.IOException;
62 import java.util.ArrayList;
63
64 public class PackageInstallerSession extends IPackageInstallerSession.Stub {
65     private static final String TAG = "PackageInstaller";
66
67     private final PackageInstallerService.Callback mCallback;
68     private final PackageManagerService mPm;
69     private final Handler mHandler;
70
71     public final int sessionId;
72     public final int userId;
73     public final String installerPackageName;
74     /** UID not persisted */
75     public final int installerUid;
76     public final PackageInstallerParams params;
77     public final long createdMillis;
78     public final File sessionDir;
79
80     private static final int MSG_INSTALL = 0;
81
82     private Handler.Callback mHandlerCallback = new Handler.Callback() {
83         @Override
84         public boolean handleMessage(Message msg) {
85             synchronized (mLock) {
86                 if (msg.obj != null) {
87                     mRemoteObserver = (IPackageInstallObserver2) msg.obj;
88                 }
89
90                 try {
91                     installLocked();
92                 } catch (InstallFailedException e) {
93                     Slog.e(TAG, "Install failed: " + e);
94                     try {
95                         mRemoteObserver.packageInstalled(mPackageName, null, e.error);
96                     } catch (RemoteException ignored) {
97                     }
98                 }
99
100                 return true;
101             }
102         }
103     };
104
105     private final Object mLock = new Object();
106
107     private int mProgress;
108
109     private String mPackageName;
110     private int mVersionCode;
111     private Signature[] mSignatures;
112
113     private boolean mMutationsAllowed;
114     private boolean mVerifierConfirmed;
115     private boolean mPermissionsConfirmed;
116     private boolean mInvalid;
117
118     private ArrayList<FileBridge> mBridges = new ArrayList<>();
119
120     private IPackageInstallObserver2 mRemoteObserver;
121
122     public PackageInstallerSession(PackageInstallerService.Callback callback,
123             PackageManagerService pm, int sessionId, int userId, String installerPackageName,
124             int installerUid, PackageInstallerParams params, long createdMillis, File sessionDir,
125             Looper looper) {
126         mCallback = callback;
127         mPm = pm;
128         mHandler = new Handler(looper, mHandlerCallback);
129
130         this.sessionId = sessionId;
131         this.userId = userId;
132         this.installerPackageName = installerPackageName;
133         this.installerUid = installerUid;
134         this.params = params;
135         this.createdMillis = createdMillis;
136         this.sessionDir = sessionDir;
137
138         // Check against any explicitly provided signatures
139         mSignatures = params.signatures;
140
141         // TODO: splice in flag when restoring persisted session
142         mMutationsAllowed = true;
143
144         if (pm.checkPermission(android.Manifest.permission.INSTALL_PACKAGES, installerPackageName)
145                 == PackageManager.PERMISSION_GRANTED) {
146             mPermissionsConfirmed = true;
147         }
148     }
149
150     @Override
151     public void updateProgress(int progress) {
152         mProgress = progress;
153         mCallback.onProgressChanged(this);
154     }
155
156     @Override
157     public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
158         // TODO: relay over to DCS when installing to ASEC
159
160         // Quick sanity check of state, and allocate a pipe for ourselves. We
161         // then do heavy disk allocation outside the lock, but this open pipe
162         // will block any attempted install transitions.
163         final FileBridge bridge;
164         synchronized (mLock) {
165             if (!mMutationsAllowed) {
166                 throw new IllegalStateException("Mutations not allowed");
167             }
168
169             bridge = new FileBridge();
170             mBridges.add(bridge);
171         }
172
173         try {
174             // Use installer provided name for now; we always rename later
175             if (!FileUtils.isValidExtFilename(name)) {
176                 throw new IllegalArgumentException("Invalid name: " + name);
177             }
178             final File target = new File(sessionDir, name);
179
180             final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
181                     OsConstants.O_CREAT | OsConstants.O_WRONLY, 00700);
182
183             // If caller specified a total length, allocate it for them. Free up
184             // cache space to grow, if needed.
185             if (lengthBytes > 0) {
186                 final StructStat stat = Libcore.os.fstat(targetFd);
187                 final long deltaBytes = lengthBytes - stat.st_size;
188                 if (deltaBytes > 0) {
189                     mPm.freeStorage(deltaBytes);
190                 }
191                 Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
192             }
193
194             if (offsetBytes > 0) {
195                 Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
196             }
197
198             bridge.setTargetFile(targetFd);
199             bridge.start();
200             return new ParcelFileDescriptor(bridge.getClientSocket());
201
202         } catch (ErrnoException e) {
203             throw new IllegalStateException("Failed to write", e);
204         } catch (IOException e) {
205             throw new IllegalStateException("Failed to write", e);
206         }
207     }
208
209     @Override
210     public void install(IPackageInstallObserver2 observer) {
211         Preconditions.checkNotNull(observer);
212         mHandler.obtainMessage(MSG_INSTALL, observer).sendToTarget();
213     }
214
215     private void installLocked() throws InstallFailedException {
216         if (mInvalid) {
217             throw new InstallFailedException(INSTALL_FAILED_ALREADY_EXISTS, "Invalid session");
218         }
219
220         // Verify that all writers are hands-off
221         if (mMutationsAllowed) {
222             for (FileBridge bridge : mBridges) {
223                 if (!bridge.isClosed()) {
224                     throw new InstallFailedException(INSTALL_FAILED_PACKAGE_CHANGED,
225                             "Files still open");
226                 }
227             }
228             mMutationsAllowed = false;
229
230             // TODO: persist disabled mutations before going forward, since
231             // beyond this point we may have hardlinks to the valid install
232         }
233
234         // Verify that stage looks sane with respect to existing application.
235         // This currently only ensures packageName, versionCode, and certificate
236         // consistency.
237         validateInstallLocked();
238
239         Preconditions.checkNotNull(mPackageName);
240         Preconditions.checkNotNull(mSignatures);
241
242         if (!mVerifierConfirmed) {
243             // TODO: async communication with verifier
244             // when they confirm, we'll kick off another install() pass
245             mVerifierConfirmed = true;
246         }
247
248         if (!mPermissionsConfirmed) {
249             // TODO: async confirm permissions with user
250             // when they confirm, we'll kick off another install() pass
251             mPermissionsConfirmed = true;
252         }
253
254         // Unpack any native libraries contained in this session
255         unpackNativeLibraries();
256
257         // Inherit any packages and native libraries from existing install that
258         // haven't been overridden.
259         if (!params.fullInstall) {
260             spliceExistingFilesIntoStage();
261         }
262
263         // TODO: for ASEC based applications, grow and stream in packages
264
265         // We've reached point of no return; call into PMS to install the stage.
266         // Regardless of success or failure we always destroy session.
267         final IPackageInstallObserver2 remoteObserver = mRemoteObserver;
268         final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
269             @Override
270             public void packageInstalled(String basePackageName, Bundle extras, int returnCode)
271                     throws RemoteException {
272                 destroy();
273                 remoteObserver.packageInstalled(basePackageName, extras, returnCode);
274             }
275         };
276
277         mPm.installStage(mPackageName, this.sessionDir, localObserver, params.installFlags);
278     }
279
280     /**
281      * Validate install by confirming that all application packages are have
282      * consistent package name, version code, and signing certificates.
283      * <p>
284      * Renames package files in stage to match split names defined inside.
285      */
286     private void validateInstallLocked() throws InstallFailedException {
287         mPackageName = null;
288         mVersionCode = -1;
289         mSignatures = null;
290
291         final File[] files = sessionDir.listFiles();
292         if (ArrayUtils.isEmpty(files)) {
293             throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, "No packages staged");
294         }
295
296         final ArraySet<String> seenSplits = new ArraySet<>();
297
298         // Verify that all staged packages are internally consistent
299         for (File file : files) {
300             final PackageLite info = PackageParser.parsePackageLite(file.getAbsolutePath(),
301                     PackageParser.PARSE_GET_SIGNATURES);
302             if (info == null) {
303                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
304                         "Failed to parse " + file);
305             }
306
307             if (!seenSplits.add(info.splitName)) {
308                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
309                         "Split " + info.splitName + " was defined multiple times");
310             }
311
312             // Use first package to define unknown values
313             if (mPackageName != null) {
314                 mPackageName = info.packageName;
315                 mVersionCode = info.versionCode;
316             }
317             if (mSignatures != null) {
318                 mSignatures = info.signatures;
319             }
320
321             assertPackageConsistent(String.valueOf(file), info.packageName, info.versionCode,
322                     info.signatures);
323
324             // Take this opportunity to enforce uniform naming
325             final String name;
326             if (info.splitName == null) {
327                 name = info.packageName + ".apk";
328             } else {
329                 name = info.packageName + "-" + info.splitName + ".apk";
330             }
331             if (!FileUtils.isValidExtFilename(name)) {
332                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
333                         "Invalid filename: " + name);
334             }
335             if (!file.getName().equals(name)) {
336                 file.renameTo(new File(file.getParentFile(), name));
337             }
338         }
339
340         // TODO: shift package signature verification to installer; we're
341         // currently relying on PMS to do this.
342         // TODO: teach about compatible upgrade keysets.
343
344         if (params.fullInstall) {
345             // Full installs must include a base package
346             if (!seenSplits.contains(null)) {
347                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
348                         "Full install must include a base package");
349             }
350
351         } else {
352             // Partial installs must be consistent with existing install.
353             final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
354             if (app == null) {
355                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
356                         "Missing existing base package for " + mPackageName);
357             }
358
359             final PackageLite info = PackageParser.parsePackageLite(app.sourceDir,
360                     PackageParser.PARSE_GET_SIGNATURES);
361             if (info == null) {
362                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
363                         "Failed to parse existing base " + app.sourceDir);
364             }
365
366             assertPackageConsistent("Existing base", info.packageName, info.versionCode,
367                     info.signatures);
368         }
369     }
370
371     private void assertPackageConsistent(String tag, String packageName, int versionCode,
372             Signature[] signatures) throws InstallFailedException {
373         if (!mPackageName.equals(packageName)) {
374             throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, tag + " package "
375                     + packageName + " inconsistent with " + mPackageName);
376         }
377         if (mVersionCode != versionCode) {
378             throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, tag
379                     + " version code " + versionCode + " inconsistent with "
380                     + mVersionCode);
381         }
382         if (!Signature.areExactMatch(mSignatures, signatures)) {
383             throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
384                     tag + " signatures are inconsistent");
385         }
386     }
387
388     /**
389      * Application is already installed; splice existing files that haven't been
390      * overridden into our stage.
391      */
392     private void spliceExistingFilesIntoStage() throws InstallFailedException {
393         final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
394         final File existingDir = new File(app.sourceDir).getParentFile();
395
396         try {
397             linkTreeIgnoringExisting(existingDir, sessionDir);
398         } catch (ErrnoException e) {
399             throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR,
400                     "Failed to splice into stage");
401         }
402     }
403
404     /**
405      * Recursively hard link all files from source directory tree to target.
406      * When a file already exists in the target tree, it leaves that file
407      * intact.
408      */
409     private void linkTreeIgnoringExisting(File sourceDir, File targetDir) throws ErrnoException {
410         final File[] sourceContents = sourceDir.listFiles();
411         if (ArrayUtils.isEmpty(sourceContents)) return;
412
413         for (File sourceFile : sourceContents) {
414             final File targetFile = new File(targetDir, sourceFile.getName());
415
416             if (sourceFile.isDirectory()) {
417                 targetFile.mkdir();
418                 linkTreeIgnoringExisting(sourceFile, targetFile);
419             } else {
420                 Libcore.os.link(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath());
421             }
422         }
423     }
424
425     private void unpackNativeLibraries() throws InstallFailedException {
426         final File libDir = new File(sessionDir, "lib");
427
428         if (!libDir.mkdir()) {
429             throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR,
430                     "Failed to create " + libDir);
431         }
432
433         try {
434             Libcore.os.chmod(libDir.getAbsolutePath(), 0755);
435         } catch (ErrnoException e) {
436             throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR,
437                     "Failed to prepare " + libDir + ": " + e);
438         }
439
440         if (!SELinux.restorecon(libDir)) {
441             throw new InstallFailedException(INSTALL_FAILED_INTERNAL_ERROR,
442                     "Failed to set context on " + libDir);
443         }
444
445         // Unpack all native libraries under stage
446         final File[] files = sessionDir.listFiles();
447         if (ArrayUtils.isEmpty(files)) {
448             throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, "No packages staged");
449         }
450
451         for (File file : files) {
452             final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(file);
453             try {
454                 final int abiIndex = NativeLibraryHelper.findSupportedAbi(handle,
455                         Build.SUPPORTED_ABIS);
456                 if (abiIndex >= 0) {
457                     int copyRet = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle, libDir,
458                             Build.SUPPORTED_ABIS[abiIndex]);
459                     if (copyRet != INSTALL_SUCCEEDED) {
460                         throw new InstallFailedException(copyRet,
461                                 "Failed to copy native libraries for " + file);
462                     }
463                 } else if (abiIndex != PackageManager.NO_NATIVE_LIBRARIES) {
464                     throw new InstallFailedException(abiIndex,
465                             "Failed to copy native libraries for " + file);
466                 }
467             } finally {
468                 handle.close();
469             }
470         }
471     }
472
473     @Override
474     public void destroy() {
475         try {
476             synchronized (mLock) {
477                 mInvalid = true;
478             }
479             FileUtils.deleteContents(sessionDir);
480             sessionDir.delete();
481         } finally {
482             mCallback.onSessionInvalid(this);
483         }
484     }
485
486     private class InstallFailedException extends Exception {
487         private final int error;
488
489         public InstallFailedException(int error, String detailMessage) {
490             super(detailMessage);
491             this.error = error;
492         }
493     }
494 }