Merge "AppsOnSd feature - Add default container"
This commit is contained in:
committed by
Android (Google) Code Review
commit
e5651d6701
@@ -136,6 +136,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/speech/tts/ITtsCallback.aidl \
|
||||
core/java/com/android/internal/app/IBatteryStats.aidl \
|
||||
core/java/com/android/internal/app/IUsageStats.aidl \
|
||||
core/java/com/android/internal/app/IMediaContainerService.aidl \
|
||||
core/java/com/android/internal/appwidget/IAppWidgetService.aidl \
|
||||
core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
|
||||
core/java/com/android/internal/backup/IBackupTransport.aidl \
|
||||
|
||||
@@ -33,6 +33,7 @@ int create_pkg_path(char path[PKG_PATH_MAX],
|
||||
}
|
||||
|
||||
x = pkgname;
|
||||
int alpha = -1;
|
||||
while (*x) {
|
||||
if (isalnum(*x) || (*x == '_')) {
|
||||
/* alphanumeric or underscore are fine */
|
||||
@@ -42,13 +43,28 @@ int create_pkg_path(char path[PKG_PATH_MAX],
|
||||
LOGE("invalid package name '%s'\n", pkgname);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
} else if (*x == '-') {
|
||||
/* Suffix -X is fine to let versioning of packages.
|
||||
But whatever follows should be alphanumeric.*/
|
||||
alpha = 1;
|
||||
}else {
|
||||
/* anything not A-Z, a-z, 0-9, _, or . is invalid */
|
||||
LOGE("invalid package name '%s'\n", pkgname);
|
||||
return -1;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
if (alpha == 1) {
|
||||
// Skip current character
|
||||
x++;
|
||||
while (*x) {
|
||||
if (!isalnum(*x)) {
|
||||
LOGE("invalid package name '%s' should include only numbers after -\n", pkgname);
|
||||
return -1;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(path, "%s%s%s", prefix, pkgname, postfix);
|
||||
return 0;
|
||||
|
||||
28
core/java/com/android/internal/app/IMediaContainerService.aidl
Executable file
28
core/java/com/android/internal/app/IMediaContainerService.aidl
Executable file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.internal.app;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
interface IMediaContainerService {
|
||||
String copyResourceToContainer(in Uri packageURI,
|
||||
String containerId,
|
||||
String key, String resFileName);
|
||||
boolean copyResource(in Uri packageURI,
|
||||
in ParcelFileDescriptor outStream);
|
||||
}
|
||||
@@ -1202,6 +1202,16 @@
|
||||
android:description="@string/permdesc_cache_filesystem"
|
||||
android:protectionLevel="signatureOrSystem" />
|
||||
|
||||
<!-- Must be required by default container service so that only
|
||||
the system can bind to it and use it to copy
|
||||
protected data to secure containers or files
|
||||
accessible to the system.
|
||||
@hide -->
|
||||
<permission android:name="android.permission.COPY_PROTECTED_DATA"
|
||||
android:label="@string/permlab_copyProtectedData"
|
||||
android:description="@string/permlab_copyProtectedData"
|
||||
android:protectionLevel="signature" />
|
||||
|
||||
<application android:process="system"
|
||||
android:persistent="true"
|
||||
android:hasCode="false"
|
||||
|
||||
@@ -2102,6 +2102,12 @@
|
||||
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||
<string name="permdesc_pkgUsageStats">Allows the modification of collected component usage statistics. Not for use by normal applications.</string>
|
||||
|
||||
<!-- permission attributes related to default container service -->
|
||||
<!-- Title of an application permission that lets an application use default container service. -->
|
||||
<string name="permlab_copyProtectedData">Allows to invoke default container service to copy content. Not for use by normal applications.</string>
|
||||
<!-- Description of an application permission, used to invoke default container service to copy content. -->
|
||||
<string name="permdesc_copyProtectedData">Allows to invoke default container service to copy content. Not for use by normal applications.</string>
|
||||
|
||||
<!-- Shown in the tutorial for tap twice for zoom control. -->
|
||||
<string name="tutorial_double_tap_to_zoom_message_short">Tap twice for zoom control</string>
|
||||
|
||||
|
||||
12
packages/DefaultContainerService/Android.mk
Executable file
12
packages/DefaultContainerService/Android.mk
Executable file
@@ -0,0 +1,12 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-subdir-java-files)
|
||||
|
||||
LOCAL_PACKAGE_NAME := DefaultContainerService
|
||||
|
||||
LOCAL_CERTIFICATE := platform
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
14
packages/DefaultContainerService/AndroidManifest.xml
Executable file
14
packages/DefaultContainerService/AndroidManifest.xml
Executable file
@@ -0,0 +1,14 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.defcontainer">
|
||||
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"/>
|
||||
|
||||
<application android:process="def.container.service"
|
||||
android:label="@string/service_name">
|
||||
|
||||
<service android:name=".DefaultContainerService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.COPY_PROTECTED_DATA"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
23
packages/DefaultContainerService/res/values/strings.xml
Normal file
23
packages/DefaultContainerService/res/values/strings.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
**
|
||||
** Copyright 2008, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
-->
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<!-- service name -->
|
||||
<string name="service_name">Media Container Service</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,305 @@
|
||||
package com.android.defcontainer;
|
||||
|
||||
import com.android.internal.app.IMediaContainerService;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Debug;
|
||||
import android.os.IBinder;
|
||||
import android.os.IMountService;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.app.Service;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import android.os.FileUtils;
|
||||
|
||||
|
||||
/*
|
||||
* This service copies a downloaded apk to a file passed in as
|
||||
* a ParcelFileDescriptor or to a newly created container specified
|
||||
* by parameters. The DownloadManager gives access to this process
|
||||
* based on its uid. This process also needs the ACCESS_DOWNLOAD_MANAGER
|
||||
* permission to access apks downloaded via the download manager.
|
||||
*/
|
||||
public class DefaultContainerService extends Service {
|
||||
private static final String TAG = "DefContainer";
|
||||
private static final boolean localLOGV = false;
|
||||
|
||||
private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
|
||||
/*
|
||||
* Creates a new container and copies resource there.
|
||||
* @param paackageURI the uri of resource to be copied. Can be either
|
||||
* a content uri or a file uri
|
||||
* @param containerId the id of the secure container that should
|
||||
* be used for creating a secure container into which the resource
|
||||
* will be copied.
|
||||
* @param key Refers to key used for encrypting the secure container
|
||||
* @param resFileName Name of the target resource file(relative to newly
|
||||
* created secure container)
|
||||
* @return Returns the new cache path where the resource has been copied into
|
||||
*
|
||||
*/
|
||||
public String copyResourceToContainer(final Uri packageURI,
|
||||
final String containerId,
|
||||
final String key, final String resFileName) {
|
||||
if (packageURI == null || containerId == null) {
|
||||
return null;
|
||||
}
|
||||
return copyResourceInner(packageURI, containerId, key, resFileName);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy specified resource to output stream
|
||||
* @param packageURI the uri of resource to be copied. Should be a
|
||||
* file uri
|
||||
* @param outStream Remote file descriptor to be used for copying
|
||||
* @return Returns true if copy succeded or false otherwise.
|
||||
*/
|
||||
public boolean copyResource(final Uri packageURI,
|
||||
ParcelFileDescriptor outStream) {
|
||||
if (packageURI == null || outStream == null) {
|
||||
return false;
|
||||
}
|
||||
ParcelFileDescriptor.AutoCloseOutputStream
|
||||
autoOut = new ParcelFileDescriptor.AutoCloseOutputStream(outStream);
|
||||
return copyFile(packageURI, autoOut);
|
||||
}
|
||||
};
|
||||
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
private IMountService getMountService() {
|
||||
return IMountService.Stub.asInterface(ServiceManager.getService("mount"));
|
||||
}
|
||||
|
||||
private String copyResourceInner(Uri packageURI, String newCacheId, String key, String resFileName) {
|
||||
// Create new container at newCachePath
|
||||
String codePath = packageURI.getPath();
|
||||
String newCachePath = null;
|
||||
final int CREATE_FAILED = 1;
|
||||
final int COPY_FAILED = 2;
|
||||
final int FINALIZE_FAILED = 3;
|
||||
final int PASS = 4;
|
||||
int errCode = CREATE_FAILED;
|
||||
// Create new container
|
||||
if ((newCachePath = createSdDir(packageURI, newCacheId, key)) != null) {
|
||||
File resFile = new File(newCachePath, resFileName);
|
||||
errCode = COPY_FAILED;
|
||||
if (localLOGV) Log.i(TAG, "Trying to copy " + codePath + " to " + resFile);
|
||||
// Copy file from codePath
|
||||
if (FileUtils.copyFile(new File(codePath), resFile)) {
|
||||
errCode = FINALIZE_FAILED;
|
||||
if (finalizeSdDir(newCacheId)) {
|
||||
errCode = PASS;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Print error based on errCode
|
||||
String errMsg = "";
|
||||
switch (errCode) {
|
||||
case CREATE_FAILED:
|
||||
errMsg = "CREATE_FAILED";
|
||||
break;
|
||||
case COPY_FAILED:
|
||||
errMsg = "COPY_FAILED";
|
||||
destroySdDir(newCacheId);
|
||||
break;
|
||||
case FINALIZE_FAILED:
|
||||
errMsg = "FINALIZE_FAILED";
|
||||
destroySdDir(newCacheId);
|
||||
break;
|
||||
default:
|
||||
errMsg = "PASS";
|
||||
unMountSdDir(newCacheId);
|
||||
break;
|
||||
}
|
||||
Log.i(TAG, "Status: " + errMsg);
|
||||
if (errCode != PASS) {
|
||||
return null;
|
||||
}
|
||||
return newCachePath;
|
||||
}
|
||||
|
||||
private String createSdDir(final Uri packageURI,
|
||||
String containerId, String sdEncKey) {
|
||||
File tmpPackageFile = new File(packageURI.getPath());
|
||||
// Create mount point via MountService
|
||||
IMountService mountService = getMountService();
|
||||
long len = tmpPackageFile.length();
|
||||
int mbLen = (int) (len/(1024*1024));
|
||||
if ((len - (mbLen * 1024 * 1024)) > 0) {
|
||||
mbLen++;
|
||||
}
|
||||
if (localLOGV) Log.i(TAG, "mbLen="+mbLen);
|
||||
String cachePath = null;
|
||||
int ownerUid = Process.myUid();
|
||||
try {
|
||||
cachePath = mountService.createSecureContainer(containerId,
|
||||
mbLen,
|
||||
"vfat", sdEncKey, ownerUid);
|
||||
if (localLOGV) Log.i(TAG, "Trying to create secure container for "
|
||||
+ containerId + ", cachePath =" + cachePath);
|
||||
return cachePath;
|
||||
} catch(IllegalStateException e) {
|
||||
Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG, "MounteService not running?");
|
||||
return null;
|
||||
}
|
||||
// TODO just fail here and let the user delete later on.
|
||||
try {
|
||||
mountService.destroySecureContainer(containerId);
|
||||
if (localLOGV) Log.i(TAG, "Destroying cache for " + containerId
|
||||
+ ", cachePath =" + cachePath);
|
||||
} catch(IllegalStateException e) {
|
||||
Log.e(TAG, "Failed to destroy existing cache: " + e);
|
||||
return null;
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG, "MounteService not running?");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
cachePath = mountService.createSecureContainer(containerId,
|
||||
mbLen,
|
||||
"vfat", sdEncKey, ownerUid);
|
||||
if (localLOGV) Log.i(TAG, "Trying to install again " + containerId
|
||||
+ ", cachePath =" + cachePath);
|
||||
return cachePath;
|
||||
} catch(IllegalStateException e) {
|
||||
Log.e(TAG, "Failed to create storage on sdcard with exception: " + e);
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG, "MounteService not running?");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean destroySdDir(String containerId) {
|
||||
try {
|
||||
// We need to destroy right away
|
||||
getMountService().destroySecureContainer(containerId);
|
||||
return true;
|
||||
} catch (IllegalStateException e) {
|
||||
Log.i(TAG, "Failed to destroy container : " + containerId);
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG, "MounteService not running?");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean finalizeSdDir(String containerId){
|
||||
try {
|
||||
getMountService().finalizeSecureContainer(containerId);
|
||||
return true;
|
||||
} catch (IllegalStateException e) {
|
||||
Log.i(TAG, "Failed to finalize container for pkg : " + containerId);
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG, "MounteService not running?");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean unMountSdDir(String containerId) {
|
||||
try {
|
||||
getMountService().unmountSecureContainer(containerId);
|
||||
return true;
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Failed to unmount id: " + containerId + " with exception " + e);
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG, "MounteService not running?");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private String mountSdDir(String containerId, String key) {
|
||||
try {
|
||||
return getMountService().mountSecureContainer(containerId, key, Process.myUid());
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "Failed to mount id: " +
|
||||
containerId + " with exception " + e);
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG, "MounteService not running?");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean copyToFile(InputStream inputStream, FileOutputStream out) {
|
||||
try {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.i(TAG, "Exception : " + e + " when copying file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean copyToFile(File srcFile, FileOutputStream out) {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = new FileInputStream(srcFile);
|
||||
return copyToFile(inputStream, out);
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
} finally {
|
||||
try { if (inputStream != null) inputStream.close(); } catch (IOException e) {}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean copyFile(Uri pPackageURI, FileOutputStream outStream) {
|
||||
if (pPackageURI.getScheme().equals("file")) {
|
||||
final File srcPackageFile = new File(pPackageURI.getPath());
|
||||
// We copy the source package file to a temp file and then rename it to the
|
||||
// destination file in order to eliminate a window where the package directory
|
||||
// scanner notices the new package file but it's not completely copied yet.
|
||||
if (!copyToFile(srcPackageFile, outStream)) {
|
||||
Log.e(TAG, "Couldn't copy file: " + srcPackageFile);
|
||||
return false;
|
||||
}
|
||||
} else if (pPackageURI.getScheme().equals("content")) {
|
||||
ParcelFileDescriptor fd = null;
|
||||
try {
|
||||
fd = getContentResolver().openFileDescriptor(pPackageURI, "r");
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(TAG, "Couldn't open file descriptor from download service. Failed with exception " + e);
|
||||
return false;
|
||||
}
|
||||
if (fd == null) {
|
||||
Log.e(TAG, "Couldn't open file descriptor from download service (null).");
|
||||
return false;
|
||||
} else {
|
||||
if (localLOGV) {
|
||||
Log.v(TAG, "Opened file descriptor from download service.");
|
||||
}
|
||||
ParcelFileDescriptor.AutoCloseInputStream
|
||||
dlStream = new ParcelFileDescriptor.AutoCloseInputStream(fd);
|
||||
// We copy the source package file to a temp file and then rename it to the
|
||||
// destination file in order to eliminate a window where the package directory
|
||||
// scanner notices the new package file but it's not completely copied yet.
|
||||
if (!copyToFile(dlStream, outStream)) {
|
||||
Log.e(TAG, "Couldn't copy " + pPackageURI + " to temp file.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Package URI is not 'file:' or 'content:' - " + pPackageURI);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -263,10 +263,15 @@ class Installer {
|
||||
return execute(builder.toString());
|
||||
}
|
||||
|
||||
public int setForwardLockPerm(String packageName, int gid) {
|
||||
/*
|
||||
* @param packagePathSuffix The name of the path relative to install
|
||||
* directory. Say if the path name is /data/app/com.test-1.apk,
|
||||
* the package suffix path will be com.test-1
|
||||
*/
|
||||
public int setForwardLockPerm(String packagePathSuffix, int gid) {
|
||||
StringBuilder builder = new StringBuilder("protect");
|
||||
builder.append(' ');
|
||||
builder.append(packageName);
|
||||
builder.append(packagePathSuffix);
|
||||
builder.append(' ');
|
||||
builder.append(gid);
|
||||
return execute(builder.toString());
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user