Merge "Cleaned up the old test cases and test media files."
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 17f8adb..63f3258 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -278,7 +278,6 @@
int token, IBackupManager callbackBinder) throws RemoteException {
long ident = Binder.clearCallingIdentity();
try {
-Log.d(TAG, "doRestoreFile() => onRestoreFile()");
BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
} catch (IOException e) {
throw new RuntimeException(e);
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index dfb0dd7..3b70e19 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -46,7 +46,7 @@
public static final String SHARED_STORAGE_TOKEN = "shared";
public static final String APPS_PREFIX = "apps/";
- public static final String SHARED_PREFIX = "shared/";
+ public static final String SHARED_PREFIX = SHARED_STORAGE_TOKEN + "/";
public static final String FULL_BACKUP_INTENT_ACTION = "fullback";
public static final String FULL_RESTORE_INTENT_ACTION = "fullrest";
@@ -61,7 +61,8 @@
String linkdomain, String rootpath, String path, BackupDataOutput output);
static public void restoreToFile(ParcelFileDescriptor data,
- long size, int type, long mode, long mtime, File outFile) throws IOException {
+ long size, int type, long mode, long mtime, File outFile,
+ boolean doChmod) throws IOException {
if (type == FullBackup.TYPE_DIRECTORY) {
// Canonically a directory has no associated content, so we don't need to read
// anything from the pipe in this case. Just create the directory here and
@@ -116,7 +117,7 @@
}
// Now twiddle the state to match the backup, assuming all went well
- if (outFile != null) {
+ if (doChmod && outFile != null) {
try {
Libcore.os.chmod(outFile.getPath(), (int)mode);
} catch (ErrnoException e) {
diff --git a/core/java/android/app/backup/FullBackupAgent.java b/core/java/android/app/backup/FullBackupAgent.java
index 4dca593..df1c363 100644
--- a/core/java/android/app/backup/FullBackupAgent.java
+++ b/core/java/android/app/backup/FullBackupAgent.java
@@ -28,8 +28,6 @@
import libcore.io.StructStat;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
@@ -84,9 +82,10 @@
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState) {
+ ParcelFileDescriptor newState) throws IOException {
// Filters, the scan queue, and the set of resulting entities
HashSet<String> filterSet = new HashSet<String>();
+ String packageName = getPackageName();
// Okay, start with the app's root tree, but exclude all of the canonical subdirs
if (mLibDir != null) {
@@ -96,25 +95,28 @@
filterSet.add(mDatabaseDir);
filterSet.add(mSharedPrefsDir);
filterSet.add(mFilesDir);
- processTree(FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
+ processTree(packageName, FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
// Now do the same for the files dir, db dir, and shared prefs dir
filterSet.add(mMainDir);
filterSet.remove(mFilesDir);
- processTree(FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
+ processTree(packageName, FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
filterSet.add(mFilesDir);
filterSet.remove(mDatabaseDir);
- processTree(FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
+ processTree(packageName, FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
filterSet.add(mDatabaseDir);
filterSet.remove(mSharedPrefsDir);
- processTree(FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
+ processTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
}
- private void processTree(String domain, String rootPath,
+ // Scan the dir tree (if it actually exists) and process each entry we find. If the
+ // 'excludes' parameter is non-null, it is consulted each time a new file system entity
+ // is visited to see whether that entity (and its subtree, if appropriate) should be
+ // omitted from the backup process.
+ protected void processTree(String packageName, String domain, String rootPath,
HashSet<String> excludes, BackupDataOutput data) {
- // Scan the dir tree (if it actually exists) and process each entry we find
File rootFile = new File(rootPath);
if (rootFile.exists()) {
LinkedList<File> scanQueue = new LinkedList<File>();
@@ -125,7 +127,7 @@
String filePath = file.getAbsolutePath();
// prune this subtree?
- if (excludes.contains(filePath)) {
+ if (excludes != null && excludes.contains(filePath)) {
continue;
}
@@ -149,7 +151,7 @@
}
// Finally, back this file up before proceeding
- FullBackup.backupToTar(getPackageName(), domain, null, rootPath, filePath, data);
+ FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, data);
}
}
}
@@ -218,6 +220,6 @@
if (DEBUG) Log.i(TAG, "[" + domain + " : " + relpath + "] mapped to " + outFile.getPath());
// Now that we've figured out where the data goes, send it on its way
- FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
+ FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
}
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 09fede0..31ad6e9 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1517,11 +1517,12 @@
}
}
+ // fullBackupAgent is explicitly handled even if allowBackup is false
name = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_fullBackupAgent, 0);
if (name != null) {
ai.fullBackupAgentName = buildClassName(pkgName, name, outError);
- if (true) {
+ if (false) {
Log.v(TAG, "android:fullBackupAgent=" + ai.fullBackupAgentName
+ " from " + pkgName + "+" + name);
}
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index bc4208a..792e4c1 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -32,6 +32,7 @@
private final boolean mRemovable;
private final boolean mEmulated;
private final int mMtpReserveSpace;
+ private final boolean mAllowMassStorage;
private int mStorageId;
// StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
@@ -39,23 +40,25 @@
// ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
- public StorageVolume(String path, String description,
- boolean removable, boolean emulated, int mtpReserveSpace) {
+ public StorageVolume(String path, String description, boolean removable,
+ boolean emulated, int mtpReserveSpace, boolean allowMassStorage) {
mPath = path;
mDescription = description;
mRemovable = removable;
mEmulated = emulated;
mMtpReserveSpace = mtpReserveSpace;
+ mAllowMassStorage = allowMassStorage;
}
// for parcelling only
- private StorageVolume(String path, String description,
- boolean removable, boolean emulated, int mtpReserveSpace, int storageId) {
+ private StorageVolume(String path, String description, boolean removable,
+ boolean emulated, int mtpReserveSpace, int storageId, boolean allowMassStorage) {
mPath = path;
mDescription = description;
mRemovable = removable;
mEmulated = emulated;
mMtpReserveSpace = mtpReserveSpace;
+ mAllowMassStorage = allowMassStorage;
mStorageId = storageId;
}
@@ -130,6 +133,15 @@
return mMtpReserveSpace;
}
+ /**
+ * Returns true if this volume can be shared via USB mass storage.
+ *
+ * @return whether mass storage is allowed
+ */
+ public boolean allowMassStorage() {
+ return mAllowMassStorage;
+ }
+
@Override
public boolean equals(Object obj) {
if (obj instanceof StorageVolume && mPath != null) {
@@ -158,9 +170,10 @@
int emulated = in.readInt();
int storageId = in.readInt();
int mtpReserveSpace = in.readInt();
+ int allowMassStorage = in.readInt();
return new StorageVolume(path, description,
removable == 1, emulated == 1,
- mtpReserveSpace, storageId);
+ mtpReserveSpace, storageId, allowMassStorage == 1);
}
public StorageVolume[] newArray(int size) {
@@ -179,5 +192,6 @@
parcel.writeInt(mEmulated ? 1 : 0);
parcel.writeInt(mStorageId);
parcel.writeInt(mMtpReserveSpace);
+ parcel.writeInt(mAllowMassStorage ? 1 : 0);
}
}
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 2df2688..a06d0f6 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -183,6 +183,15 @@
*/
public static final String DIRTY = "dirty";
+ /**
+ * If set to 1 this causes events on this calendar to be duplicated with
+ * {@link EventsColumns#LAST_SYNCED} set to 1 whenever the event transitions from non-dirty
+ * to dirty. The duplicated event will not be expanded in the instances table and will only
+ * show up in sync adapter queries of the events table. It will also be deleted when the
+ * originating event has its dirty flag cleared by the sync adapter.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String CAN_PARTIALLY_UPDATE = "canPartiallyUpdate";
}
/**
@@ -317,7 +326,8 @@
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
Calendars.DISPLAY_NAME);
- DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.CALENDAR_COLOR);
+ DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
+ Calendars.CALENDAR_COLOR);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBLE);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SYNC_EVENTS);
@@ -332,6 +342,8 @@
Calendars.CAN_MODIFY_TIME_ZONE);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
Calendars.MAX_REMINDERS);
+ DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
+ Calendars.CAN_PARTIALLY_UPDATE);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
@@ -512,6 +524,7 @@
MAX_REMINDERS,
CAN_MODIFY_TIME_ZONE,
CAN_ORGANIZER_RESPOND,
+ CAN_PARTIALLY_UPDATE,
CALENDAR_LOCATION,
CALENDAR_TIMEZONE,
ACCESS_LEVEL,
@@ -686,6 +699,23 @@
public static final String SYNC_DATA1 = "sync_data1";
/**
+ * This column is available for use by sync adapters
+ * <P>Type: TEXT</P>
+ */
+ public static final String SYNC_DATA7 = "sync_data7";
+
+ /**
+ * Used to indicate that a row is not a real event but an original copy of a locally
+ * modified event. A copy is made when an event changes from non-dirty to dirty and the
+ * event is on a calendar with {@link Calendars#CAN_PARTIALLY_UPDATE} set to 1. This copy
+ * does not get expanded in the instances table and is only visible in queries made by a
+ * sync adapter. The copy gets removed when the event is changed back to non-dirty by a
+ * sync adapter.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String LAST_SYNCED = "lastSynced";
+
+ /**
* The comments feed uri. Column name.
* TODO change to sync_data6
* <P>Type: TEXT</P>
@@ -1030,7 +1060,9 @@
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORGANIZER);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_DATA);
+ DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, SYNC_DATA7);
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
+ DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, LAST_SYNCED);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC1);
@@ -1191,7 +1223,8 @@
CAL_SYNC3,
CAL_SYNC4,
CAL_SYNC5,
- CAL_SYNC6
+ CAL_SYNC6,
+ CAN_PARTIALLY_UPDATE,
};
/**
@@ -1706,9 +1739,9 @@
public static final String NOTIFY_TIME = "notifyTime";
/**
- * The state of this alert. It starts out as {@link SCHEDULED}, then
- * when the alarm goes off, it changes to {@link FIRED}, and then when
- * the user dismisses the alarm it changes to {@link DISMISSED}. Column
+ * The state of this alert. It starts out as {@link #SCHEDULED}, then
+ * when the alarm goes off, it changes to {@link #FIRED}, and then when
+ * the user dismisses the alarm it changes to {@link #DISMISSED}. Column
* name.
* <P>Type: INTEGER</P>
*/
diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp
index ecfe5ff..b36fa3e 100644
--- a/core/jni/android_app_backup_FullBackup.cpp
+++ b/core/jni/android_app_backup_FullBackup.cpp
@@ -73,6 +73,8 @@
static int backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
jstring domainObj, jstring linkdomain,
jstring rootpathObj, jstring pathObj, jobject dataOutputObj) {
+ int ret;
+
// Extract the various strings, allowing for null object pointers
const char* packagenamechars = env->GetStringUTFChars(packageNameObj, NULL);
const char* rootchars = env->GetStringUTFChars(rootpathObj, NULL);
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9c55627..94a9063 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5156,6 +5156,8 @@
<!-- number of megabytes of storage MTP should reserve for free storage
(used for emulated storage that is shared with system's data partition) -->
<attr name="mtpReserve" format="integer" />
+ <!-- true if the storage can be shared via USB mass storage -->
+ <attr name="allowMassStorage" format="boolean" />
</declare-styleable>
</resources>
diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp
index f933199f..b433fd3 100644
--- a/libs/utils/BackupHelpers.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -503,10 +503,10 @@
needExtended = true;
}
- // Non-7bit-clean path also means needing pax extended format
+ // Non-7bit-clean path or embedded spaces also mean needing pax extended format
if (!needExtended) {
for (size_t i = 0; i < filepath.length(); i++) {
- if ((filepath[i] & 0x80) != 0) {
+ if ((filepath[i] & 0x80) != 0 || filepath[i] == ' ') {
needExtended = true;
break;
}
diff --git a/packages/SharedStorageBackup/Android.mk b/packages/SharedStorageBackup/Android.mk
new file mode 100644
index 0000000..1d4f4da7
--- /dev/null
+++ b/packages/SharedStorageBackup/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2011 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_PACKAGE_NAME := SharedStorageBackup
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+########################
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/packages/SharedStorageBackup/AndroidManifest.xml b/packages/SharedStorageBackup/AndroidManifest.xml
new file mode 100644
index 0000000..258059c
--- /dev/null
+++ b/packages/SharedStorageBackup/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2011, 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sharedstoragebackup" >
+
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
+
+ <application android:allowClearUserData="false"
+ android:permission="android.permission.CONFIRM_FULL_BACKUP"
+ android:fullBackupAgent=".SharedStorageAgent"
+ android:allowBackup="false" >
+ </application>
+</manifest>
diff --git a/packages/SharedStorageBackup/proguard.flags b/packages/SharedStorageBackup/proguard.flags
new file mode 100644
index 0000000..f43cb81
--- /dev/null
+++ b/packages/SharedStorageBackup/proguard.flags
@@ -0,0 +1 @@
+-keep class com.android.sharedstoragebackup.SharedStorageAgent
diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
new file mode 100644
index 0000000..b02ca2e
--- /dev/null
+++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
@@ -0,0 +1,93 @@
+package com.android.sharedstoragebackup;
+
+import android.app.backup.FullBackup;
+import android.app.backup.FullBackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.content.Context;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+
+public class SharedStorageAgent extends FullBackupAgent {
+ static final String TAG = "SharedStorageAgent";
+ static final boolean DEBUG = true;
+
+ StorageVolume[] mVolumes;
+
+ @Override
+ public void onCreate() {
+ StorageManager mgr = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
+ if (mgr != null) {
+ mVolumes = mgr.getVolumeList();
+ } else {
+ Slog.e(TAG, "Unable to access Storage Manager");
+ }
+ }
+
+ @Override
+ public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) throws IOException {
+ // If there are shared-storage volumes available, run the inherited directory-
+ // hierarchy backup process on them. By convention in the Storage Manager, the
+ // "primary" shared storage volume is first in the list.
+ if (mVolumes != null) {
+ for (int i = 0; i < mVolumes.length; i++) {
+ StorageVolume v = mVolumes[i];
+ // Express the contents of volume N this way in the tar stream:
+ // shared/N/path/to/file
+ // The restore will then extract to the given volume
+ String domain = FullBackup.SHARED_PREFIX + i;
+ processTree(null, domain, v.getPath(), null, data);
+ }
+ }
+ }
+
+ /**
+ * Incremental onRestore() implementation is not used.
+ */
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+ throws IOException {
+ }
+
+ /**
+ * Full restore of one file to shared storage
+ */
+ @Override
+ public void onRestoreFile(ParcelFileDescriptor data, long size,
+ int type, String domain, String relpath, long mode, long mtime)
+ throws IOException {
+ Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]");
+
+ File outFile = null;
+
+ // The file path must be in the semantic form [number]/path/to/file...
+ int slash = relpath.indexOf('/');
+ if (slash > 0) {
+ try {
+ int i = Integer.parseInt(relpath.substring(0, slash));
+ if (i <= mVolumes.length) {
+ outFile = new File(mVolumes[i].getPath(), relpath.substring(slash + 1));
+ if (DEBUG) Slog.i(TAG, " => " + outFile.getAbsolutePath());
+ } else {
+ Slog.w(TAG, "Cannot restore data for unavailable volume " + i);
+ }
+ } catch (NumberFormatException e) {
+ if (DEBUG) Slog.w(TAG, "Bad volume number token: " + relpath.substring(0, slash));
+ }
+ } else {
+ if (DEBUG) Slog.i(TAG, "Can't find volume-number token");
+ }
+ if (outFile == null) {
+ Slog.e(TAG, "Skipping data with malformed path " + relpath);
+ }
+
+ FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, false);
+ }
+}
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png
new file mode 100644
index 0000000..e2584e3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png
new file mode 100644
index 0000000..58b8510
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png
new file mode 100644
index 0000000..2795c34
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png
new file mode 100644
index 0000000..bbed6a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml
new file mode 100644
index 0000000..977e002
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_zoom_pressed" />
+ <item android:drawable="@drawable/ic_sysbar_zoom_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
index d9f3f23..707a8cb 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
@@ -75,6 +75,13 @@
systemui:keyCode="82"
android:visibility="invisible"
/>
+ <com.android.systemui.statusbar.policy.CompatModeButton
+ android:id="@+id/compat_button"
+ android:layout_width="80dip"
+ android:layout_height="match_parent"
+ android:src="@drawable/ic_sysbar_zoom"
+ android:visibility="invisible"
+ />
</LinearLayout>
<!-- fake space bar zone -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
new file mode 100644
index 0000000..9b44f78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
@@ -0,0 +1,63 @@
+/*
+ * 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.systemui.statusbar.policy;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+public class CompatModeButton extends ImageView implements View.OnClickListener {
+ private static final String TAG = "StatusBar.CompatModeButton";
+
+ private ActivityManager mAM;
+
+ public CompatModeButton(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CompatModeButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs);
+
+ setClickable(true);
+
+ mAM = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+
+ setOnClickListener(this);
+
+ refresh();
+ }
+
+ @Override
+ public void onClick(View v) {
+ mAM.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_TOGGLE);
+ }
+
+ public void refresh() {
+ setVisibility(
+ (mAM.getFrontActivityScreenCompatMode() == ActivityManager.COMPAT_MODE_NEVER)
+ ? View.GONE
+ : View.VISIBLE
+ );
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 5b5801d..ffb45ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -69,6 +69,7 @@
import com.android.systemui.statusbar.*;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CompatModeButton;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.Prefs;
@@ -963,6 +964,10 @@
// See above re: lights-out policy for legacy apps.
if (visible) setLightsOn(true);
+
+ // XXX: HACK: not sure if this is the best way to catch a new activity that might require a
+ // change in compatibility features, but it's a start.
+ ((CompatModeButton) mBarContents.findViewById(R.id.compat_button)).refresh();
}
public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 7c6d3c1..b568af1 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -134,6 +134,7 @@
// Timeout intervals for agent backup & restore operations
static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
+ static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
// User confirmation timeout for a full backup/restore operation
@@ -1691,7 +1692,7 @@
public void run() {
final List<PackageInfo> packagesToBackup;
- Slog.i(TAG, "--- Performing full-dataset restore ---");
+ Slog.i(TAG, "--- Performing full-dataset backup ---");
sendStartBackup();
// doAllApps supersedes the package set if any
@@ -1720,64 +1721,23 @@
}
}
- // Now back up the app data via the agent mechanism
PackageInfo pkg = null;
try {
+ // Now back up the app data via the agent mechanism
int N = packagesToBackup.size();
for (int i = 0; i < N; i++) {
pkg = packagesToBackup.get(i);
+ backupOnePackage(pkg);
+ }
- Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
-
- IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
- IApplicationThread.BACKUP_MODE_FULL);
- if (agent != null) {
- try {
- ApplicationInfo app = pkg.applicationInfo;
- boolean sendApk = mIncludeApks
- && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
- && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
- (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
-
- sendOnBackupPackage(pkg.packageName);
-
- {
- BackupDataOutput output = new BackupDataOutput(
- mOutputFile.getFileDescriptor());
-
- if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
- writeAppManifest(pkg, mManifestFile, sendApk);
- FullBackup.backupToTar(pkg.packageName, null, null,
- mFilesDir.getAbsolutePath(),
- mManifestFile.getAbsolutePath(),
- output);
- }
-
- if (DEBUG) Slog.d(TAG, "Calling doBackup()");
- final int token = generateToken();
- prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
- agent.doBackup(null, mOutputFile, null, sendApk,
- token, mBackupManagerBinder);
- boolean success = waitUntilOperationComplete(token);
- if (!success) {
- Slog.d(TAG, "Full backup failed on package " + pkg.packageName);
- } else {
- if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
- }
- } catch (IOException e) {
- Slog.e(TAG, "Error backing up " + pkg.packageName, e);
- }
- } else {
- Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
- }
- tearDown(pkg);
+ // Finally, shared storage if requested
+ if (mIncludeShared) {
+ backupSharedStorage();
}
} catch (RemoteException e) {
Slog.e(TAG, "App died during full backup");
} finally {
- if (pkg != null) {
- tearDown(pkg);
- }
+ tearDown(pkg);
try {
mOutputFile.close();
} catch (IOException e) {
@@ -1796,6 +1756,79 @@
}
}
+ private void backupOnePackage(PackageInfo pkg) throws RemoteException {
+ Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
+
+ IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+ IApplicationThread.BACKUP_MODE_FULL);
+ if (agent != null) {
+ try {
+ ApplicationInfo app = pkg.applicationInfo;
+ boolean sendApk = mIncludeApks
+ && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
+ && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
+ (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+
+ sendOnBackupPackage(pkg.packageName);
+
+ {
+ BackupDataOutput output = new BackupDataOutput(
+ mOutputFile.getFileDescriptor());
+
+ if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
+ writeAppManifest(pkg, mManifestFile, sendApk);
+ FullBackup.backupToTar(pkg.packageName, null, null,
+ mFilesDir.getAbsolutePath(),
+ mManifestFile.getAbsolutePath(),
+ output);
+ }
+
+ if (DEBUG) Slog.d(TAG, "Calling doBackup()");
+ final int token = generateToken();
+ prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
+ agent.doBackup(null, mOutputFile, null, sendApk,
+ token, mBackupManagerBinder);
+ if (!waitUntilOperationComplete(token)) {
+ Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Error backing up " + pkg.packageName, e);
+ }
+ } else {
+ Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
+ }
+ tearDown(pkg);
+ }
+
+ private void backupSharedStorage() throws RemoteException {
+ PackageInfo pkg = null;
+ try {
+ pkg = mPackageManager.getPackageInfo("com.android.sharedstoragebackup", 0);
+ IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+ IApplicationThread.BACKUP_MODE_FULL);
+ if (agent != null) {
+ sendOnBackupPackage("Shared storage");
+
+ final int token = generateToken();
+ prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL);
+ agent.doBackup(null, mOutputFile, null, false, token, mBackupManagerBinder);
+ if (!waitUntilOperationComplete(token)) {
+ Slog.e(TAG, "Full backup failed on shared storage");
+ } else {
+ if (DEBUG) Slog.d(TAG, "Full shared storage backup success");
+ }
+ } else {
+ Slog.e(TAG, "Could not bind to shared storage backup agent");
+ }
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Shared storage backup package not found");
+ } finally {
+ tearDown(pkg);
+ }
+ }
+
private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
throws IOException {
// Manifest format. All data are strings ending in LF:
@@ -1836,23 +1869,24 @@
}
private void tearDown(PackageInfo pkg) {
- final ApplicationInfo app = pkg.applicationInfo;
- try {
- // unbind and tidy up even on timeout or failure, just in case
- mActivityManager.unbindBackupAgent(app);
+ if (pkg != null) {
+ final ApplicationInfo app = pkg.applicationInfo;
+ if (app != null) {
+ try {
+ // unbind and tidy up even on timeout or failure, just in case
+ mActivityManager.unbindBackupAgent(app);
- // The agent was running with a stub Application object, so shut it down.
- // !!! We hardcode the confirmation UI's package name here rather than use a
- // manifest flag! TODO something less direct.
- if (app.uid != Process.SYSTEM_UID
- && !pkg.packageName.equals("com.android.backupconfirm")) {
- if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
- mActivityManager.killApplicationProcess(app.processName, app.uid);
- } else {
- if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+ // The agent was running with a stub Application object, so shut it down.
+ if (app.uid != Process.SYSTEM_UID) {
+ if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
+ mActivityManager.killApplicationProcess(app.processName, app.uid);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Lost app trying to shut down");
+ }
}
- } catch (RemoteException e) {
- Slog.d(TAG, "Lost app trying to shut down");
}
}
@@ -1949,6 +1983,7 @@
// with a whitelist of packages known to be unclearable.
mClearedPackages.add("android");
mClearedPackages.add("com.android.providers.settings");
+
}
class RestoreFileRunnable implements Runnable {
@@ -1988,6 +2023,11 @@
Slog.i(TAG, "--- Performing full-dataset restore ---");
sendStartRestore();
+ // Are we able to restore shared-storage data?
+ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ mPackagePolicies.put("com.android.sharedstoragebackup", RestorePolicy.ACCEPT);
+ }
+
try {
byte[] buffer = new byte[32 * 1024];
FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor());
@@ -2707,7 +2747,9 @@
info.path, 0, FullBackup.SHARED_PREFIX.length())) {
// File in shared storage. !!! TODO: implement this.
info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
+ info.packageName = "com.android.sharedstoragebackup";
info.domain = FullBackup.SHARED_STORAGE_TOKEN;
+ if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
} else if (FullBackup.APPS_PREFIX.regionMatches(0,
info.path, 0, FullBackup.APPS_PREFIX.length())) {
// App content! Parse out the package name and domain
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index f78dca9..c86f962 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1121,16 +1121,20 @@
com.android.internal.R.styleable.Storage_emulated, false);
int mtpReserve = a.getInt(
com.android.internal.R.styleable.Storage_mtpReserve, 0);
+ boolean allowMassStorage = a.getBoolean(
+ com.android.internal.R.styleable.Storage_allowMassStorage, false);
Slog.d(TAG, "got storage path: " + path + " description: " + description +
" primary: " + primary + " removable: " + removable +
- " emulated: " + emulated + " mtpReserve: " + mtpReserve);
+ " emulated: " + emulated + " mtpReserve: " + mtpReserve +
+ " allowMassStorage: " + allowMassStorage);
if (path == null || description == null) {
Slog.e(TAG, "path or description is null in readStorageList");
} else {
String pathString = path.toString();
StorageVolume volume = new StorageVolume(pathString,
- description.toString(), removable, emulated, mtpReserve);
+ description.toString(), removable, emulated,
+ mtpReserve, allowMassStorage);
if (primary) {
if (mPrimaryVolume == null) {
mPrimaryVolume = volume;
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index 99c8af6..08c6699 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -138,7 +138,7 @@
if (outFile == null) {
Slog.w(TAG, "Skipping unrecognized system file: [ " + domain + " : " + path + " ]");
}
- FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
+ FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
if (restoredWallpaper) {
WallpaperManagerService wallpaper =