Merge "AppsOnSd feature - Add default container"

This commit is contained in:
Suchi Amalapurapu
2010-01-27 10:40:26 -08:00
committed by Android (Google) Code Review
11 changed files with 1106 additions and 269 deletions

View File

@@ -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 \

View File

@@ -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;

View 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);
}

View File

@@ -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"

View File

@@ -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>

View 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)

View 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>

View 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>

View File

@@ -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;
}
}

View File

@@ -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