Merge change 4527 into donut
* changes:
Fix Canvas.finalize() for the case where the constructor throws an exception before the native canvas instance was created.
diff --git a/api/current.xml b/api/current.xml
index 2128e5b..a5476fb 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -27473,6 +27473,17 @@
visibility="public"
>
</method>
+<method name="getApplicationInfo"
+ return="android.content.pm.ApplicationInfo"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getAssets"
return="android.content.res.AssetManager"
abstract="true"
@@ -28825,6 +28836,17 @@
visibility="public"
>
</method>
+<method name="getApplicationInfo"
+ return="android.content.pm.ApplicationInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getAssets"
return="android.content.res.AssetManager"
abstract="false"
@@ -30186,7 +30208,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="uri" type="java.lang.String">
@@ -30237,6 +30259,17 @@
<parameter name="defaultValue" type="long">
</parameter>
</method>
+<method name="getPackage"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getParcelableArrayExtra"
return="android.os.Parcelable[]"
abstract="false"
@@ -30436,6 +30469,23 @@
<exception name="XmlPullParserException" type="org.xmlpull.v1.XmlPullParserException">
</exception>
</method>
+<method name="parseUri"
+ return="android.content.Intent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="java.lang.String">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+<exception name="URISyntaxException" type="java.net.URISyntaxException">
+</exception>
+</method>
<method name="putExtra"
return="android.content.Intent"
abstract="false"
@@ -31109,6 +31159,19 @@
<parameter name="flags" type="int">
</parameter>
</method>
+<method name="setPackage"
+ return="android.content.Intent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+</method>
<method name="setType"
return="android.content.Intent"
abstract="false"
@@ -31129,9 +31192,22 @@
synchronized="false"
static="false"
final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+</method>
+<method name="toUri"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
+<parameter name="flags" type="int">
+</parameter>
</method>
<method name="writeToParcel"
return="void"
@@ -31874,6 +31950,17 @@
visibility="public"
>
</field>
+<field name="ACTION_SEND_MULTIPLE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.intent.action.SEND_MULTIPLE""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_SET_WALLPAPER"
type="java.lang.String"
transient="false"
@@ -32500,6 +32587,17 @@
visibility="public"
>
</field>
+<field name="FILL_IN_PACKAGE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_ACTIVITY_BROUGHT_TO_FRONT"
type="int"
transient="false"
@@ -32709,6 +32807,17 @@
visibility="public"
>
</field>
+<field name="URI_INTENT_SCHEME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="Intent.FilterComparison"
extends="java.lang.Object"
@@ -35109,6 +35218,17 @@
visibility="public"
>
</field>
+<field name="FLAG_SUPPORTS_LARGE_SCREENS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="512"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_SYSTEM"
type="int"
transient="false"
@@ -35182,16 +35302,6 @@
visibility="public"
>
</field>
-<field name="expandable"
- type="boolean"
- transient="false"
- volatile="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="flags"
type="int"
transient="false"
@@ -36910,23 +37020,6 @@
<parameter name="flags" type="int">
</parameter>
</method>
-<method name="resolveActivity"
- return="android.content.pm.ResolveInfo"
- abstract="true"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="intent" type="android.content.Intent">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
-<parameter name="packageName" type="java.lang.String">
-</parameter>
-</method>
<method name="resolveContentProvider"
return="android.content.pm.ProviderInfo"
abstract="true"
@@ -37068,17 +37161,6 @@
visibility="public"
>
</field>
-<field name="GET_EXPANDABLE"
- type="int"
- transient="false"
- volatile="false"
- value="131072"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="GET_GIDS"
type="int"
transient="false"
@@ -114328,6 +114410,17 @@
visibility="public"
>
</method>
+<method name="getApplicationInfo"
+ return="android.content.pm.ApplicationInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getAssets"
return="android.content.res.AssetManager"
abstract="false"
@@ -115702,23 +115795,6 @@
</parameter>
<parameter name="flags" type="int">
</parameter>
-<parameter name="packageName" type="java.lang.String">
-</parameter>
-</method>
-<method name="resolveActivity"
- return="android.content.pm.ResolveInfo"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-<parameter name="intent" type="android.content.Intent">
-</parameter>
-<parameter name="flags" type="int">
-</parameter>
</method>
<method name="resolveContentProvider"
return="android.content.pm.ProviderInfo"
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 3af80fa..841e3df 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -80,6 +80,14 @@
doRestore();
return;
}
+
+ if ("transport".equals(op)) {
+ doTransport();
+ return;
+ }
+
+ System.err.println("Unknown command");
+ showUsage();
}
private void doRun() {
@@ -113,6 +121,19 @@
}
}
+ private void doTransport() {
+ try {
+ int which = Integer.parseInt(nextArg());
+ int old = mBmgr.selectBackupTransport(which);
+ System.out.println("Selected transport " + which + " (formerly " + old + ")");
+ } catch (NumberFormatException e) {
+ showUsage();
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+ }
+
private void doList() {
String arg = nextArg(); // sets, transports, packages set#
if ("transports".equals(arg)) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 501be01..51a8ed2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -864,13 +864,24 @@
final Integer dialogId = ids[i];
Bundle dialogState = b.getBundle(savedDialogKeyFor(dialogId));
if (dialogState != null) {
- final Dialog dialog = onCreateDialog(dialogId);
- dialog.onRestoreInstanceState(dialogState);
+ final Dialog dialog = createDialog(dialogId);
mManagedDialogs.put(dialogId, dialog);
+ onPrepareDialog(dialogId, dialog);
+ dialog.onRestoreInstanceState(dialogState);
}
}
}
+ private Dialog createDialog(Integer dialogId) {
+ final Dialog dialog = onCreateDialog(dialogId);
+ if (dialog == null) {
+ throw new IllegalArgumentException("Activity#onCreateDialog did "
+ + "not create a dialog for id " + dialogId);
+ }
+ dialog.dispatchOnCreate(null);
+ return dialog;
+ }
+
private String savedDialogKeyFor(int key) {
return SAVED_DIALOG_KEY_PREFIX + key;
}
@@ -2419,12 +2430,7 @@
}
Dialog dialog = mManagedDialogs.get(id);
if (dialog == null) {
- dialog = onCreateDialog(id);
- if (dialog == null) {
- throw new IllegalArgumentException("Activity#onCreateDialog did "
- + "not create a dialog for id " + id);
- }
- dialog.dispatchOnCreate(null);
+ dialog = createDialog(id);
mManagedDialogs.put(id, dialog);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 477badb..6873379 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -187,7 +187,7 @@
try {
appInfo = getPackageManager().getApplicationInfo(
pkgInfo.getPackageName(),
- PackageManager.GET_SUPPORTS_DENSITIES | PackageManager.GET_EXPANDABLE);
+ PackageManager.GET_SUPPORTS_DENSITIES);
} catch (RemoteException e) {
throw new AssertionError(e);
}
@@ -287,6 +287,10 @@
return mPackageName;
}
+ public ApplicationInfo getApplicationInfo() {
+ return mApplicationInfo;
+ }
+
public boolean isSecurityViolation() {
return mSecurityViolation;
}
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index fa3d5c2..bbad8f4 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -283,6 +283,14 @@
}
@Override
+ public ApplicationInfo getApplicationInfo() {
+ if (mPackageInfo != null) {
+ return mPackageInfo.getApplicationInfo();
+ }
+ throw new RuntimeException("Not supported in system context");
+ }
+
+ @Override
public String getPackageResourcePath() {
if (mPackageInfo != null) {
return mPackageInfo.getResDir();
@@ -1519,14 +1527,16 @@
// overall package (such as if it has multiple launcher entries).
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(Intent.CATEGORY_INFO);
- ResolveInfo resolveInfo = resolveActivity(intentToResolve, 0, packageName);
+ intentToResolve.setPackage(packageName);
+ ResolveInfo resolveInfo = resolveActivity(intentToResolve, 0);
// Otherwise, try to find a main launcher activity.
if (resolveInfo == null) {
// reuse the intent instance
intentToResolve.removeCategory(Intent.CATEGORY_INFO);
intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
- resolveInfo = resolveActivity(intentToResolve, 0, packageName);
+ intentToResolve.setPackage(packageName);
+ resolveInfo = resolveActivity(intentToResolve, 0);
}
if (resolveInfo == null) {
return null;
@@ -1774,19 +1784,6 @@
}
@Override
- public ResolveInfo resolveActivity(Intent intent, int flags, String packageName) {
- try {
- return mPM.resolveIntentForPackage(
- intent,
- intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags,
- packageName);
- } catch (RemoteException e) {
- throw new RuntimeException("Package manager has died", e);
- }
- }
-
- @Override
public List<ResolveInfo> queryIntentActivities(Intent intent,
int flags) {
try {
diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java
index 997bfdc..85c001c 100644
--- a/core/java/android/app/BackupAgent.java
+++ b/core/java/android/app/BackupAgent.java
@@ -17,6 +17,7 @@
package android.app;
import android.app.IBackupAgent;
+import android.backup.BackupDataInput;
import android.backup.BackupDataOutput;
import android.content.Context;
import android.content.ContextWrapper;
@@ -25,6 +26,8 @@
import android.os.RemoteException;
import android.util.Log;
+import java.io.IOException;
+
/**
* This is the central interface between an application and Android's
* settings backup mechanism.
@@ -32,6 +35,8 @@
* @hide pending API solidification
*/
public abstract class BackupAgent extends ContextWrapper {
+ private static final String TAG = "BackupAgent";
+
public BackupAgent() {
super(null);
}
@@ -77,8 +82,8 @@
* file. The application should record the final backup state
* here after restoring its data from dataFd.
*/
- public abstract void onRestore(ParcelFileDescriptor /* TODO: BackupDataInput */ data,
- ParcelFileDescriptor newState);
+ public abstract void onRestore(BackupDataInput data, ParcelFileDescriptor newState)
+ throws IOException;
// ----- Core implementation -----
@@ -107,13 +112,11 @@
ParcelFileDescriptor newState) throws RemoteException {
// !!! TODO - real implementation; for now just invoke the callbacks directly
Log.v(TAG, "doBackup() invoked");
- BackupDataOutput output = new BackupDataOutput(BackupAgent.this,
- data.getFileDescriptor());
+ BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
try {
BackupAgent.this.onBackup(oldState, output, newState);
} catch (RuntimeException ex) {
- Log.d("BackupAgent", "onBackup ("
- + BackupAgent.this.getClass().getName() + ") threw", ex);
+ Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
throw ex;
}
}
@@ -122,7 +125,16 @@
ParcelFileDescriptor newState) throws RemoteException {
// !!! TODO - real implementation; for now just invoke the callbacks directly
Log.v(TAG, "doRestore() invoked");
- BackupAgent.this.onRestore(data, newState);
+ BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
+ try {
+ BackupAgent.this.onRestore(input, newState);
+ } catch (IOException ex) {
+ Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw new RuntimeException(ex);
+ } catch (RuntimeException ex) {
+ Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw ex;
+ }
}
}
}
diff --git a/core/java/android/app/FullBackupAgent.java b/core/java/android/app/FullBackupAgent.java
index bf5cb5d..89becf4 100644
--- a/core/java/android/app/FullBackupAgent.java
+++ b/core/java/android/app/FullBackupAgent.java
@@ -1,5 +1,6 @@
package android.app;
+import android.backup.BackupDataInput;
import android.backup.BackupDataOutput;
import android.backup.FileBackupHelper;
import android.os.ParcelFileDescriptor;
@@ -52,6 +53,6 @@
}
@Override
- public void onRestore(ParcelFileDescriptor data, ParcelFileDescriptor newState) {
+ public void onRestore(BackupDataInput data, ParcelFileDescriptor newState) {
}
}
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index b6c8385..3a3a983 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -134,9 +134,7 @@
private SuggestionsAdapter mSuggestionsAdapter;
// Whether to rewrite queries when selecting suggestions
- // TODO: This is disabled because of problems with persistent selections
- // causing non-user-initiated rewrites.
- private static final boolean REWRITE_QUERIES = false;
+ private static final boolean REWRITE_QUERIES = true;
// The query entered by the user. This is not changed when selecting a suggestion
// that modifies the contents of the text field. But if the user then edits
diff --git a/core/java/android/backup/BackupDataInputStream.java b/core/java/android/backup/BackupDataInputStream.java
index 52b1675..b705c4c 100644
--- a/core/java/android/backup/BackupDataInputStream.java
+++ b/core/java/android/backup/BackupDataInputStream.java
@@ -16,6 +16,8 @@
package android.backup;
+import android.util.Log;
+
import java.io.InputStream;
import java.io.IOException;
diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/backup/BackupDataOutput.java
index 1348d81..05e667e 100644
--- a/core/java/android/backup/BackupDataOutput.java
+++ b/core/java/android/backup/BackupDataOutput.java
@@ -24,13 +24,11 @@
/** @hide */
public class BackupDataOutput {
int mBackupWriter;
- private Context mContext;
public static final int OP_UPDATE = 1;
public static final int OP_DELETE = 2;
- public BackupDataOutput(Context context, FileDescriptor fd) {
- mContext = context;
+ public BackupDataOutput(FileDescriptor fd) {
if (fd == null) throw new NullPointerException();
mBackupWriter = ctor(fd);
if (mBackupWriter == 0) {
@@ -38,6 +36,7 @@
}
}
+ // A dataSize of -1 indicates that the record under this key should be deleted
public int writeEntityHeader(String key, int dataSize) throws IOException {
int result = writeEntityHeader_native(mBackupWriter, key, dataSize);
if (result >= 0) {
diff --git a/core/java/android/backup/FileRestoreHelper.java b/core/java/android/backup/FileRestoreHelper.java
new file mode 100644
index 0000000..b7e3625
--- /dev/null
+++ b/core/java/android/backup/FileRestoreHelper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2009 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 android.backup;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.File;
+
+/** @hide */
+public class FileRestoreHelper extends RestoreHelperBase implements RestoreHelper {
+ private static final String TAG = "FileRestoreHelper";
+
+ File mFilesDir;
+
+ public FileRestoreHelper(Context context) {
+ super(context);
+ mFilesDir = context.getFilesDir();
+ }
+
+ public void restoreEntity(BackupDataInputStream data) {
+ Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size()); // TODO: turn this off before ship
+ File f = new File(mFilesDir, data.getKey());
+ writeFile(f, data);
+ }
+}
+
diff --git a/core/java/android/backup/RestoreHelper.java b/core/java/android/backup/RestoreHelper.java
index ee8bedd..e47869c 100644
--- a/core/java/android/backup/RestoreHelper.java
+++ b/core/java/android/backup/RestoreHelper.java
@@ -16,6 +16,8 @@
package android.backup;
+import android.os.ParcelFileDescriptor;
+
import java.io.InputStream;
/** @hide */
@@ -26,6 +28,7 @@
* Do not close the <code>data</code> stream. Do not read more than
* <code>dataSize</code> bytes from <code>data</code>.
*/
- public void performRestore(BackupDataInputStream data);
+ public void restoreEntity(BackupDataInputStream data);
+ public void writeSnapshot(ParcelFileDescriptor fd);
}
diff --git a/core/java/android/backup/RestoreHelperBase.java b/core/java/android/backup/RestoreHelperBase.java
new file mode 100644
index 0000000..93a8fef
--- /dev/null
+++ b/core/java/android/backup/RestoreHelperBase.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 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 android.backup;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.InputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+
+class RestoreHelperBase {
+ private static final String TAG = "RestoreHelperBase";
+
+ int mPtr;
+ Context mContext;
+ boolean mExceptionLogged;
+
+ RestoreHelperBase(Context context) {
+ mPtr = ctor();
+ mContext = context;
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ dtor(mPtr);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ void writeFile(File f, InputStream in) {
+ if (!(in instanceof BackupDataInputStream)) {
+ throw new IllegalStateException("input stream must be a BackupDataInputStream");
+ }
+ int result = -1;
+
+ // Create the enclosing directory.
+ File parent = f.getParentFile();
+ parent.mkdirs();
+
+ result = writeFile_native(mPtr, f.getAbsolutePath(),
+ ((BackupDataInputStream)in).mData.mBackupReader);
+ if (result != 0) {
+ // Bail on this entity. Only log one failure per helper object.
+ if (!mExceptionLogged) {
+ Log.e(TAG, "Failed restoring file '" + f + "' for app '"
+ + mContext.getPackageName() + "\' result=0x"
+ + Integer.toHexString(result));
+ mExceptionLogged = true;
+ }
+ }
+ }
+
+ public void writeSnapshot(ParcelFileDescriptor fd) {
+ int result = writeSnapshot_native(mPtr, fd.getFileDescriptor());
+ // TODO: Do something with the error.
+ }
+
+ private static native int ctor();
+ private static native void dtor(int ptr);
+ private static native int writeFile_native(int ptr, String filename, int backupReader);
+ private static native int writeSnapshot_native(int ptr, FileDescriptor fd);
+}
+
+
diff --git a/core/java/android/backup/RestoreHelperDispatcher.java b/core/java/android/backup/RestoreHelperDispatcher.java
index cbfefdc..4861775 100644
--- a/core/java/android/backup/RestoreHelperDispatcher.java
+++ b/core/java/android/backup/RestoreHelperDispatcher.java
@@ -16,20 +16,29 @@
package android.backup;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
import java.io.IOException;
import java.util.HashMap;
+import java.util.Map;
/** @hide */
public class RestoreHelperDispatcher {
- HashMap<String,RestoreHelper> mHelpers;
+ private static final String TAG = "RestoreHelperDispatcher";
+
+ HashMap<String,RestoreHelper> mHelpers = new HashMap<String,RestoreHelper>();
public void addHelper(String keyPrefix, RestoreHelper helper) {
mHelpers.put(keyPrefix, helper);
}
- public void dispatch(BackupDataInput input) throws IOException {
+ public void dispatch(BackupDataInput input, ParcelFileDescriptor newState) throws IOException {
+ boolean alreadyComplained = false;
+
BackupDataInputStream stream = new BackupDataInputStream(input);
while (input.readNextHeader()) {
+
String rawKey = input.getKey();
int pos = rawKey.indexOf(':');
if (pos > 0) {
@@ -38,10 +47,32 @@
if (helper != null) {
stream.dataSize = input.getDataSize();
stream.key = rawKey.substring(pos+1);
- helper.performRestore(stream);
+ helper.restoreEntity(stream);
+ } else {
+ if (!alreadyComplained) {
+ Log.w(TAG, "Couldn't find helper for: '" + rawKey + "'");
+ alreadyComplained = true;
+ }
+ }
+ } else {
+ if (!alreadyComplained) {
+ Log.w(TAG, "Entity with no prefix: '" + rawKey + "'");
+ alreadyComplained = true;
}
}
input.skipEntityData(); // In case they didn't consume the data.
}
+
+ if (mHelpers.size() > 1) {
+ throw new RuntimeException("RestoreHelperDispatcher won't get your your"
+ + " data in the right order yet.");
+ }
+
+ // Write out the state files
+ for (RestoreHelper helper: mHelpers.values()) {
+ // TODO: Write a header for the state
+ helper.writeSnapshot(newState);
+ }
}
}
+
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 925249e..b0396f6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -16,6 +16,7 @@
package android.content;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
@@ -233,6 +234,9 @@
/** Return the name of this application's package. */
public abstract String getPackageName();
+ /** Return the full application info for this context's package. */
+ public abstract ApplicationInfo getApplicationInfo();
+
/**
* {@hide}
* Return the full path to this context's resource files. This is the ZIP files
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 262204e..7513b3b 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -16,6 +16,7 @@
package android.content;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
@@ -120,6 +121,11 @@
}
@Override
+ public ApplicationInfo getApplicationInfo() {
+ return mBase.getApplicationInfo();
+ }
+
+ @Override
public String getPackageResourcePath() {
return mBase.getPackageResourcePath();
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d4a7815..11538b0 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -915,6 +915,23 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SEND = "android.intent.action.SEND";
/**
+ * Activity Action: Deliver multiple data to someone else.
+ * <p>
+ * Like ACTION_SEND, except the data is multiple.
+ * <p>
+ * Input: {@link #getType} is the MIME type of the data being sent.
+ * get*ArrayListExtra can have either a {@link #EXTRA_TEXT} or {@link
+ * #EXTRA_STREAM} field, containing the data to be sent.
+ * <p>
+ * Optional standard extras, which may be interpreted by some recipients as
+ * appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC},
+ * {@link #EXTRA_BCC}, {@link #EXTRA_SUBJECT}.
+ * <p>
+ * Output: nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE";
+ /**
* Activity Action: Handle an incoming phone call.
* <p>Input: nothing.
* <p>Output: nothing.
@@ -2040,10 +2057,25 @@
public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x20000000;
// ---------------------------------------------------------------------
+ // ---------------------------------------------------------------------
+ // toUri() and parseUri() options.
+
+ /**
+ * Flag for use with {@link #toUri} and {@link #parseUri}: the URI string
+ * always has the "intent:" scheme. This syntax can be used when you want
+ * to later disambiguate between URIs that are intended to describe an
+ * Intent vs. all others that should be treated as raw URIs. When used
+ * with {@link #parseUri}, any other scheme will result in a generic
+ * VIEW action for that raw URI.
+ */
+ public static final int URI_INTENT_SCHEME = 1<<0;
+
+ // ---------------------------------------------------------------------
private String mAction;
private Uri mData;
private String mType;
+ private String mPackage;
private ComponentName mComponent;
private int mFlags;
private HashSet<String> mCategories;
@@ -2064,6 +2096,7 @@
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
+ this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
this.mFlags = o.mFlags;
if (o.mCategories != null) {
@@ -2083,6 +2116,7 @@
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
+ this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
if (o.mCategories != null) {
this.mCategories = new HashSet<String>(o.mCategories);
@@ -2183,23 +2217,50 @@
}
/**
+ * Call {@link #parseUri} with 0 flags.
+ * @deprecated Use {@link #parseUri} instead.
+ */
+ @Deprecated
+ public static Intent getIntent(String uri) throws URISyntaxException {
+ return parseUri(uri, 0);
+ }
+
+ /**
* Create an intent from a URI. This URI may encode the action,
- * category, and other intent fields, if it was returned by toURI(). If
- * the Intent was not generate by toURI(), its data will be the entire URI
- * and its action will be ACTION_VIEW.
+ * category, and other intent fields, if it was returned by
+ * {@link #toUri}.. If the Intent was not generate by toUri(), its data
+ * will be the entire URI and its action will be ACTION_VIEW.
*
* <p>The URI given here must not be relative -- that is, it must include
* the scheme and full path.
*
* @param uri The URI to turn into an Intent.
+ * @param flags Additional processing flags. Either 0 or
*
* @return Intent The newly created Intent object.
*
- * @see #toURI
+ * @throws URISyntaxException Throws URISyntaxError if the basic URI syntax
+ * it bad (as parsed by the Uri class) or the Intent data within the
+ * URI is invalid.
+ *
+ * @see #toUri
*/
- public static Intent getIntent(String uri) throws URISyntaxException {
+ public static Intent parseUri(String uri, int flags) throws URISyntaxException {
int i = 0;
try {
+ // Validate intent scheme for if requested.
+ if ((flags&URI_INTENT_SCHEME) != 0) {
+ if (!uri.startsWith("intent:")) {
+ Intent intent = new Intent(ACTION_VIEW);
+ try {
+ intent.setData(Uri.parse(uri));
+ } catch (IllegalArgumentException e) {
+ throw new URISyntaxException(uri, e.getMessage());
+ }
+ return intent;
+ }
+ }
+
// simple case
i = uri.lastIndexOf("#");
if (i == -1) return new Intent(ACTION_VIEW, Uri.parse(uri));
@@ -2211,16 +2272,15 @@
Intent intent = new Intent(ACTION_VIEW);
// fetch data part, if present
- if (i > 0) {
- intent.mData = Uri.parse(uri.substring(0, i));
- }
+ String data = i >= 0 ? uri.substring(0, i) : null;
+ String scheme = null;
i += "#Intent;".length();
// loop over contents of Intent, all name=value;
while (!uri.startsWith("end", i)) {
int eq = uri.indexOf('=', i);
int semi = uri.indexOf(';', eq);
- String value = uri.substring(eq + 1, semi);
+ String value = Uri.decode(uri.substring(eq + 1, semi));
// action
if (uri.startsWith("action=", i)) {
@@ -2242,15 +2302,24 @@
intent.mFlags = Integer.decode(value).intValue();
}
+ // package
+ else if (uri.startsWith("package=", i)) {
+ intent.mPackage = value;
+ }
+
// component
else if (uri.startsWith("component=", i)) {
intent.mComponent = ComponentName.unflattenFromString(value);
}
+ // scheme
+ else if (uri.startsWith("scheme=", i)) {
+ scheme = value;
+ }
+
// extra
else {
String key = Uri.decode(uri.substring(i + 2, eq));
- value = Uri.decode(value);
// create Bundle if it doesn't already exist
if (intent.mExtras == null) intent.mExtras = new Bundle();
Bundle b = intent.mExtras;
@@ -2271,6 +2340,23 @@
i = semi + 1;
}
+ if (data != null) {
+ if (data.startsWith("intent:")) {
+ data = data.substring(7);
+ if (scheme != null) {
+ data = scheme + ':' + data;
+ }
+ }
+
+ if (data.length() > 0) {
+ try {
+ intent.mData = Uri.parse(data);
+ } catch (IllegalArgumentException e) {
+ throw new URISyntaxException(uri, e.getMessage());
+ }
+ }
+ }
+
return intent;
} catch (IndexOutOfBoundsException e) {
@@ -3084,6 +3170,20 @@
}
/**
+ * Retrieve the application package name this Intent is limited to. When
+ * resolving an Intent, if non-null this limits the resolution to only
+ * components in the given application package.
+ *
+ * @return The name of the application package for the Intent.
+ *
+ * @see #resolveActivity
+ * @see #setPackage
+ */
+ public String getPackage() {
+ return mPackage;
+ }
+
+ /**
* Retrieve the concrete component associated with the intent. When receiving
* an intent, this is the component that was found to best handle it (that is,
* yourself) and will always be non-null; in all other cases it will be
@@ -3118,6 +3218,9 @@
* <p>If {@link #addCategory} has added any categories, the activity must
* handle ALL of the categories specified.
*
+ * <p>If {@link #getPackage} is non-NULL, only activity components in
+ * that application package will be considered.
+ *
* <p>If there are no activities that satisfy all of these conditions, a
* null string is returned.
*
@@ -4089,6 +4192,27 @@
}
/**
+ * (Usually optional) Set an explicit application package name that limits
+ * the components this Intent will resolve to. If left to the default
+ * value of null, all components in all applications will considered.
+ * If non-null, the Intent can only match the components in the given
+ * application package.
+ *
+ * @param packageName The name of the application package to handle the
+ * intent, or null to allow any application package.
+ *
+ * @return Returns the same Intent object, for chaining multiple calls
+ * into a single statement.
+ *
+ * @see #getPackage
+ * @see #resolveActivity
+ */
+ public Intent setPackage(String packageName) {
+ mPackage = packageName;
+ return this;
+ }
+
+ /**
* (Usually optional) Explicitly set the component to handle the intent.
* If left with the default value of null, the system will determine the
* appropriate class to use based on the other fields (action, data,
@@ -4200,6 +4324,12 @@
public static final int FILL_IN_COMPONENT = 1<<3;
/**
+ * Use with {@link #fillIn} to allow the current package value to be
+ * overwritten, even if it is already set.
+ */
+ public static final int FILL_IN_PACKAGE = 1<<4;
+
+ /**
* Copy the contents of <var>other</var> in to this object, but only
* where fields are not defined by this object. For purposes of a field
* being defined, the following pieces of data in the Intent are
@@ -4210,14 +4340,15 @@
* <li> data URI and MIME type, as set by {@link #setData(Uri)},
* {@link #setType(String)}, or {@link #setDataAndType(Uri, String)}.
* <li> categories, as set by {@link #addCategory}.
+ * <li> package, as set by {@link #setPackage}.
* <li> component, as set by {@link #setComponent(ComponentName)} or
* related methods.
* <li> each top-level name in the associated extras.
* </ul>
*
* <p>In addition, you can use the {@link #FILL_IN_ACTION},
- * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, and
- * {@link #FILL_IN_COMPONENT} to override the restriction where the
+ * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE},
+ * and {@link #FILL_IN_COMPONENT} to override the restriction where the
* corresponding field will not be replaced if it is already set.
*
* <p>For example, consider Intent A with {data="foo", categories="bar"}
@@ -4233,32 +4364,39 @@
* @param flags Options to control which fields can be filled in.
*
* @return Returns a bit mask of {@link #FILL_IN_ACTION},
- * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, and
- * {@link #FILL_IN_COMPONENT} indicating which fields were changed.
+ * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE},
+ * and {@link #FILL_IN_COMPONENT} indicating which fields were changed.
*/
public int fillIn(Intent other, int flags) {
int changes = 0;
- if ((mAction == null && other.mAction == null)
- || (flags&FILL_IN_ACTION) != 0) {
+ if (other.mAction != null
+ && (mAction == null || (flags&FILL_IN_ACTION) != 0)) {
mAction = other.mAction;
changes |= FILL_IN_ACTION;
}
- if ((mData == null && mType == null &&
- (other.mData != null || other.mType != null))
- || (flags&FILL_IN_DATA) != 0) {
+ if ((other.mData != null || other.mType != null)
+ && ((mData == null && mType == null)
+ || (flags&FILL_IN_DATA) != 0)) {
mData = other.mData;
mType = other.mType;
changes |= FILL_IN_DATA;
}
- if ((mCategories == null && other.mCategories == null)
- || (flags&FILL_IN_CATEGORIES) != 0) {
+ if (other.mCategories != null
+ && (mCategories == null || (flags&FILL_IN_CATEGORIES) != 0)) {
if (other.mCategories != null) {
mCategories = new HashSet<String>(other.mCategories);
}
changes |= FILL_IN_CATEGORIES;
}
- if ((mComponent == null && other.mComponent == null)
- || (flags&FILL_IN_COMPONENT) != 0) {
+ if (other.mPackage != null
+ && (mPackage == null || (flags&FILL_IN_PACKAGE) != 0)) {
+ mPackage = other.mPackage;
+ changes |= FILL_IN_PACKAGE;
+ }
+ // Component is special: it can -only- be set if explicitly allowed,
+ // since otherwise the sender could force the intent somewhere the
+ // originator didn't intend.
+ if (other.mComponent != null && (flags&FILL_IN_COMPONENT) != 0) {
mComponent = other.mComponent;
changes |= FILL_IN_COMPONENT;
}
@@ -4373,6 +4511,17 @@
}
}
}
+ if (mPackage != other.mPackage) {
+ if (mPackage != null) {
+ if (!mPackage.equals(other.mPackage)) {
+ return false;
+ }
+ } else {
+ if (!other.mPackage.equals(mPackage)) {
+ return false;
+ }
+ }
+ }
if (mComponent != other.mComponent) {
if (mComponent != null) {
if (!mComponent.equals(other.mComponent)) {
@@ -4418,6 +4567,9 @@
if (mType != null) {
code += mType.hashCode();
}
+ if (mPackage != null) {
+ code += mPackage.hashCode();
+ }
if (mComponent != null) {
code += mComponent.hashCode();
}
@@ -4488,6 +4640,13 @@
first = false;
b.append("flg=0x").append(Integer.toHexString(mFlags));
}
+ if (mPackage != null) {
+ if (!first) {
+ b.append(' ');
+ }
+ first = false;
+ b.append("pkg=").append(mPackage);
+ }
if (comp && mComponent != null) {
if (!first) {
b.append(' ');
@@ -4504,28 +4663,87 @@
}
}
+ /**
+ * Call {@link #toUri} with 0 flags.
+ * @deprecated Use {@link #toUri} instead.
+ */
+ @Deprecated
public String toURI() {
+ return toUri(0);
+ }
+
+ /**
+ * Convert this Intent into a String holding a URI representation of it.
+ * The returned URI string has been properly URI encoded, so it can be
+ * used with {@link Uri#parse Uri.parse(String)}. The URI contains the
+ * Intent's data as the base URI, with an additional fragment describing
+ * the action, categories, type, flags, package, component, and extras.
+ *
+ * <p>You can convert the returned string back to an Intent with
+ * {@link #getIntent}.
+ *
+ * @param flags Additional operating flags. Either 0 or
+ * {@link #URI_INTENT_SCHEME}.
+ *
+ * @return Returns a URI encoding URI string describing the entire contents
+ * of the Intent.
+ */
+ public String toUri(int flags) {
StringBuilder uri = new StringBuilder(128);
- if (mData != null) uri.append(mData.toString());
+ String scheme = null;
+ if (mData != null) {
+ String data = mData.toString();
+ if ((flags&URI_INTENT_SCHEME) != 0) {
+ final int N = data.length();
+ for (int i=0; i<N; i++) {
+ char c = data.charAt(i);
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
+ || c == '.' || c == '-') {
+ continue;
+ }
+ if (c == ':' && i > 0) {
+ // Valid scheme.
+ scheme = data.substring(0, i);
+ uri.append("intent:");
+ data = data.substring(i+1);
+ break;
+ }
+
+ // No scheme.
+ break;
+ }
+ }
+ uri.append(data);
+
+ } else if ((flags&URI_INTENT_SCHEME) != 0) {
+ uri.append("intent:");
+ }
uri.append("#Intent;");
+ if (scheme != null) {
+ uri.append("scheme=").append(scheme).append(';');
+ }
if (mAction != null) {
- uri.append("action=").append(mAction).append(';');
+ uri.append("action=").append(Uri.encode(mAction)).append(';');
}
if (mCategories != null) {
for (String category : mCategories) {
- uri.append("category=").append(category).append(';');
+ uri.append("category=").append(Uri.encode(category)).append(';');
}
}
if (mType != null) {
- uri.append("type=").append(mType).append(';');
+ uri.append("type=").append(Uri.encode(mType, "/")).append(';');
}
if (mFlags != 0) {
uri.append("launchFlags=0x").append(Integer.toHexString(mFlags)).append(';');
}
+ if (mPackage != null) {
+ uri.append("package=").append(Uri.encode(mPackage)).append(';');
+ }
if (mComponent != null) {
- uri.append("component=").append(mComponent.flattenToShortString()).append(';');
+ uri.append("component=").append(Uri.encode(
+ mComponent.flattenToShortString(), "/")).append(';');
}
if (mExtras != null) {
for (String key : mExtras.keySet()) {
@@ -4567,6 +4785,7 @@
Uri.writeToParcel(out, mData);
out.writeString(mType);
out.writeInt(mFlags);
+ out.writeString(mPackage);
ComponentName.writeToParcel(mComponent, out);
if (mCategories != null) {
@@ -4600,6 +4819,7 @@
mData = Uri.CREATOR.createFromParcel(in);
mType = in.readString();
mFlags = in.readInt();
+ mPackage = in.readString();
mComponent = ComponentName.readFromParcel(in);
int N = in.readInt();
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index f10dd53..2a2cf93 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -137,6 +137,13 @@
public static final int FLAG_TEST_ONLY = 1<<8;
/**
+ * Value for {@link #flags}: true when the application's window can be
+ * expanded over default window size in target density (320x480 for
+ * 1.0 density, 480x720 for 1.5 density etc)
+ */
+ public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<9;
+
+ /**
* Value for {@link #flags}: this is false if the application has set
* its android:allowBackup to false, true otherwise.
*
@@ -201,12 +208,6 @@
public int[] supportsDensities;
/**
- * True when the application's window can be expanded over default window
- * size in target density (320x480 for 1.0 density, 480x720 for 1.5 density etc)
- */
- public boolean expandable = false;
-
- /**
* The minimum SDK version this application targets. It may run on earilier
* versions, but it knows how to work with any new behavior added at this
* version. Will be {@link android.os.Build.VERSION_CODES#CUR_DEVELOPMENT}
@@ -240,7 +241,6 @@
pw.println(prefix + "manageSpaceActivityName="+manageSpaceActivityName);
pw.println(prefix + "description=0x"+Integer.toHexString(descriptionRes));
pw.println(prefix + "supportsDensities=" + supportsDensities);
- pw.println(prefix + "expandable=" + expandable);
super.dumpBack(pw, prefix);
}
@@ -288,7 +288,6 @@
manageSpaceActivityName = orig.manageSpaceActivityName;
descriptionRes = orig.descriptionRes;
supportsDensities = orig.supportsDensities;
- expandable = orig.expandable;
}
@@ -321,7 +320,6 @@
dest.writeString(backupAgentName);
dest.writeInt(descriptionRes);
dest.writeIntArray(supportsDensities);
- dest.writeInt(expandable ? 1 : 0);
}
public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -353,7 +351,6 @@
backupAgentName = source.readString();
descriptionRes = source.readInt();
supportsDensities = source.createIntArray();
- expandable = source.readInt() != 0;
}
/**
@@ -383,7 +380,7 @@
* @hide
*/
public void disableCompatibilityMode() {
- expandable = true;
+ flags |= FLAG_SUPPORTS_LARGE_SCREENS;
supportsDensities = ANY_DENSITIES_ARRAY;
}
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1a0f31f..b33a85b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -82,9 +82,6 @@
ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags);
- ResolveInfo resolveIntentForPackage(in Intent intent, String resolvedType, int flags,
- String packageName);
-
List<ResolveInfo> queryIntentActivities(in Intent intent,
String resolvedType, int flags);
@@ -304,4 +301,11 @@
boolean isSafeMode();
void systemReady();
boolean hasSystemUidErrors();
+
+ /**
+ * Ask the package manager to perform dex-opt (if needed) on the given
+ * package, if it already hasn't done mode. Only does this if running
+ * in the special development "no pre-dexopt" mode.
+ */
+ boolean performDexOpt(String packageName);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f74f3c2..e0cad39 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -181,12 +181,6 @@
public static final int MATCH_DEFAULT_ONLY = 0x00010000;
/**
- * {@link ApplicationInfo} flag: return the
- * {link ApplicationInfo#expandable} boolean flag of the package.
- */
- public static final int GET_EXPANDABLE = 0x00020000;
-
- /**
* Permission check result: this is returned by {@link #checkPermission}
* if the permission has been granted to the given package.
*/
@@ -986,23 +980,6 @@
public abstract ResolveInfo resolveActivity(Intent intent, int flags);
/**
- * Resolve the intent restricted to a package.
- * {@see #resolveActivity}
- *
- * @param intent An intent containing all of the desired specification
- * (action, data, type, category, and/or component).
- * @param flags Additional option flags. The most important is
- * MATCH_DEFAULT_ONLY, to limit the resolution to only
- * those activities that support the CATEGORY_DEFAULT.
- * @param packageName Restrict the intent resolution to this package.
- *
- * @return Returns a ResolveInfo containing the final activity intent that
- * was determined to be the best action. Returns null if no
- * matching activity was found.
- */
- public abstract ResolveInfo resolveActivity(Intent intent, int flags, String packageName);
-
- /**
* Retrieve all activities that can be performed for the given intent.
*
* @param intent The desired intent as per resolveActivity().
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ab8559c..ab9518e 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -777,7 +777,7 @@
targetCode = minCode = val.string.toString();
} else {
// If it's not a string, it's an integer.
- minVers = val.data;
+ targetVers = minVers = val.data;
}
}
@@ -798,6 +798,25 @@
sa.recycle();
+ if (minCode != null) {
+ if (!minCode.equals(mSdkCodename)) {
+ if (mSdkCodename != null) {
+ outError[0] = "Requires development platform " + minCode
+ + " (current platform is " + mSdkCodename + ")";
+ } else {
+ outError[0] = "Requires development platform " + minCode
+ + " but this is a release platform.";
+ }
+ mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
+ return null;
+ }
+ } else if (minVers > mSdkVersion) {
+ outError[0] = "Requires newer sdk version #" + minVers
+ + " (current version is #" + mSdkVersion + ")";
+ mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
+ return null;
+ }
+
if (targetCode != null) {
if (!targetCode.equals(mSdkCodename)) {
if (mSdkCodename != null) {
@@ -817,13 +836,6 @@
pkg.applicationInfo.targetSdkVersion = targetVers;
}
- if (minVers > mSdkVersion) {
- outError[0] = "Requires newer sdk version #" + minVers
- + " (current version is #" + mSdkVersion + ")";
- mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
- return null;
- }
-
if (maxVers < mSdkVersion) {
outError[0] = "Requires older sdk version #" + maxVers
+ " (current version is #" + mSdkVersion + ")";
@@ -865,7 +877,7 @@
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("expandable")) {
- pkg.expandable = true;
+ pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
XmlUtils.skipCurrentTag(parser);
} else {
Log.w(TAG, "Bad element under <manifest>: "
@@ -2262,9 +2274,6 @@
public final ArrayList<Integer> supportsDensityList = new ArrayList<Integer>();
public int[] supportsDensities = null;
- // If the application's window is expandable.
- public boolean expandable;
-
// If this is a 3rd party app, this is the path of the zip file.
public String mPath;
@@ -2287,6 +2296,17 @@
// preferred up order.
public int mPreferredOrder = 0;
+ // For use by package manager service to keep track of which apps
+ // have been installed with forward locking.
+ public boolean mForwardLocked;
+
+ // For use by the package manager to keep track of the path to the
+ // file an app came from.
+ public String mScanPath;
+
+ // For use by package manager to keep track of where it has done dexopt.
+ public boolean mDidDexOpt;
+
// Additional data supplied by callers.
public Object mExtras;
@@ -2439,9 +2459,6 @@
&& p.supportsDensities != null) {
return true;
}
- if ((flags & PackageManager.GET_EXPANDABLE) != 0) {
- return true;
- }
return false;
}
@@ -2462,9 +2479,6 @@
if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0) {
ai.supportsDensities = p.supportsDensities;
}
- if ((flags & PackageManager.GET_EXPANDABLE) != 0) {
- ai.expandable = p.expandable;
- }
return ai;
}
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 836de39..680fef8 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -69,7 +69,8 @@
public final boolean mScalingRequired;
public CompatibilityInfo(ApplicationInfo appInfo) {
- mExpandable = mConfiguredExpandable = appInfo.expandable;
+ mExpandable = mConfiguredExpandable =
+ (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0;
float packageDensityScale = -1.0f;
if (appInfo.supportsDensities != null) {
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 2f63820..71dbd38 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -22,11 +22,9 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import android.app.ActivityThread.PackageInfo;
import android.content.pm.ApplicationInfo;
import android.graphics.Movie;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.SystemProperties;
@@ -35,6 +33,7 @@
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
+import android.util.LongSparseArray;
import java.io.IOException;
import java.io.InputStream;
@@ -59,19 +58,19 @@
// Information about preloaded resources. Note that they are not
// protected by a lock, because while preloading in zygote we are all
// single-threaded, and after that these are immutable.
- private static final SparseArray<Drawable.ConstantState> sPreloadedDrawables
- = new SparseArray<Drawable.ConstantState>();
+ private static final LongSparseArray<Drawable.ConstantState> sPreloadedDrawables
+ = new LongSparseArray<Drawable.ConstantState>();
private static final SparseArray<ColorStateList> mPreloadedColorStateLists
= new SparseArray<ColorStateList>();
private static boolean mPreloaded;
- private final SparseArray<Drawable.ConstantState> mPreloadedDrawables;
+ private final LongSparseArray<Drawable.ConstantState> mPreloadedDrawables;
/*package*/ final TypedValue mTmpValue = new TypedValue();
// These are protected by the mTmpValue lock.
- private final SparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache
- = new SparseArray<WeakReference<Drawable.ConstantState> >();
+ private final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache
+ = new LongSparseArray<WeakReference<Drawable.ConstantState> >();
private final SparseArray<WeakReference<ColorStateList> > mColorStateListCache
= new SparseArray<WeakReference<ColorStateList> >();
private boolean mPreloading;
@@ -89,20 +88,20 @@
private final CompatibilityInfo mCompatibilityInfo;
- private static final SparseArray<Object> EMPTY_ARRAY = new SparseArray<Object>() {
+ private static final LongSparseArray<Object> EMPTY_ARRAY = new LongSparseArray<Object>() {
@Override
- public void put(int k, Object o) {
+ public void put(long k, Object o) {
throw new UnsupportedOperationException();
}
@Override
- public void append(int k, Object o) {
+ public void append(long k, Object o) {
throw new UnsupportedOperationException();
}
};
@SuppressWarnings("unchecked")
- private static <T> SparseArray<T> emptySparseArray() {
- return (SparseArray<T>) EMPTY_ARRAY;
+ private static <T> LongSparseArray<T> emptySparseArray() {
+ return (LongSparseArray<T>) EMPTY_ARRAY;
}
/**
@@ -1315,14 +1314,14 @@
configChanges, cs.getChangingConfigurations())) {
if (DEBUG_CONFIG) {
Log.d(TAG, "FLUSHING #0x"
- + Integer.toHexString(mDrawableCache.keyAt(i))
+ + Long.toHexString(mDrawableCache.keyAt(i))
+ " / " + cs + " with changes: 0x"
+ Integer.toHexString(cs.getChangingConfigurations()));
}
mDrawableCache.setValueAt(i, null);
} else if (DEBUG_CONFIG) {
Log.d(TAG, "(Keeping #0x"
- + Integer.toHexString(mDrawableCache.keyAt(i))
+ + Long.toHexString(mDrawableCache.keyAt(i))
+ " / " + cs + " with changes: 0x"
+ Integer.toHexString(cs.getChangingConfigurations())
+ ")");
@@ -1653,7 +1652,7 @@
}
}
- final int key = (value.assetCookie << 24) | value.data;
+ final long key = (((long) value.assetCookie) << 32) | value.data;
Drawable dr = getCachedDrawable(key);
if (dr != null) {
@@ -1733,7 +1732,7 @@
return dr;
}
- private Drawable getCachedDrawable(int key) {
+ private Drawable getCachedDrawable(long key) {
synchronized (mTmpValue) {
WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key);
if (wr != null) { // we have the key
diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java
index 5353b53..6ea2528 100644
--- a/core/java/android/preference/PreferenceScreen.java
+++ b/core/java/android/preference/PreferenceScreen.java
@@ -22,6 +22,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Adapter;
@@ -147,13 +148,20 @@
ListView listView = new ListView(context);
bind(listView);
- Dialog dialog = mDialog = new Dialog(context, com.android.internal.R.style.Theme_NoTitleBar);
+ // Set the title bar if title is available, else no title bar
+ final CharSequence title = getTitle();
+ Dialog dialog = mDialog = new Dialog(context, !TextUtils.isEmpty(title)
+ ? com.android.internal.R.style.Theme_NoTitleBar
+ : com.android.internal.R.style.Theme);
dialog.setContentView(listView);
+ if (!TextUtils.isEmpty(title)) {
+ dialog.setTitle(title);
+ }
dialog.setOnDismissListener(this);
if (state != null) {
dialog.onRestoreInstanceState(state);
}
-
+
// Add the screen to the list of preferences screens opened as dialogs
getPreferenceManager().addPreferencesScreen(dialog);
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 8f58194..9fc143d 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -81,6 +81,28 @@
}
/**
+ * Internal constants for the TTS functionality
+ *
+ * {@hide}
+ */
+ public class Engine {
+ // default values for a TTS engine when settings are not found in the provider
+ public static final int FALLBACK_TTS_DEFAULT_RATE = 100; // 1x
+ public static final int FALLBACK_TTS_DEFAULT_PITCH = 100;// 1x
+ public static final int FALLBACK_TTS_USE_DEFAULTS = 0; // false
+ public static final String FALLBACK_TTS_DEFAULT_LANG = "eng";
+ public static final String FALLBACK_TTS_DEFAULT_COUNTRY = "";
+ public static final String FALLBACK_TTS_DEFAULT_VARIANT = "";
+
+ // return codes for a TTS engine's check data activity
+ public static final int CHECK_VOICE_DATA_PASS = 1;
+ public static final int CHECK_VOICE_DATA_FAIL = 0;
+ public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
+ public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
+ public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3;
+ }
+
+ /**
* Connection needed for the TTS.
*/
private ServiceConnection mServiceConnection;
diff --git a/core/java/android/test/InstrumentationTestCase.java b/core/java/android/test/InstrumentationTestCase.java
index 470ab0d..2145d7c 100644
--- a/core/java/android/test/InstrumentationTestCase.java
+++ b/core/java/android/test/InstrumentationTestCase.java
@@ -241,7 +241,13 @@
try {
final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key);
final int keyCode = keyCodeField.getInt(null);
- instrumentation.sendKeyDownUpSync(keyCode);
+ try {
+ instrumentation.sendKeyDownUpSync(keyCode);
+ } catch (SecurityException e) {
+ // Ignore security exceptions that are now thrown
+ // when trying to send to another app, to retain
+ // compatibility with existing tests.
+ }
} catch (NoSuchFieldException e) {
Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
break;
@@ -266,7 +272,13 @@
final Instrumentation instrumentation = getInstrumentation();
for (int i = 0; i < count; i++) {
- instrumentation.sendKeyDownUpSync(keys[i]);
+ try {
+ instrumentation.sendKeyDownUpSync(keys[i]);
+ } catch (SecurityException e) {
+ // Ignore security exceptions that are now thrown
+ // when trying to send to another app, to retain
+ // compatibility with existing tests.
+ }
}
instrumentation.waitForIdleSync();
@@ -292,7 +304,13 @@
final int keyCount = keys[i];
final int keyCode = keys[i + 1];
for (int j = 0; j < keyCount; j++) {
- instrumentation.sendKeyDownUpSync(keyCode);
+ try {
+ instrumentation.sendKeyDownUpSync(keyCode);
+ } catch (SecurityException e) {
+ // Ignore security exceptions that are now thrown
+ // when trying to send to another app, to retain
+ // compatibility with existing tests.
+ }
}
}
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index bccb3a6..1a4eb699 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -184,6 +184,9 @@
*/
public static final String HOUR_MINUTE_24 = "%H:%M";
public static final String MONTH_FORMAT = "%B";
+ /**
+ * This is not actually a useful month name in all locales.
+ */
public static final String ABBREV_MONTH_FORMAT = "%b";
public static final String NUMERIC_MONTH_FORMAT = "%m";
public static final String MONTH_DAY_FORMAT = "%-d";
@@ -1444,7 +1447,8 @@
if (numericDate) {
monthFormat = NUMERIC_MONTH_FORMAT;
} else if (abbrevMonth) {
- monthFormat = ABBREV_MONTH_FORMAT;
+ monthFormat =
+ res.getString(com.android.internal.R.string.short_format_month);
} else {
monthFormat = MONTH_FORMAT;
}
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 245148d..a095913 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -37,8 +37,7 @@
* The device's density.
* @hide
*/
- public static final int DEVICE_DENSITY = SystemProperties.getInt("ro.sf.lcd_density",
- DEFAULT_DENSITY);
+ public static final int DEVICE_DENSITY = getDeviceDensity();
/**
* The absolute width of the display in pixels.
@@ -161,4 +160,13 @@
", height=" + heightPixels + ", scaledDensity=" + scaledDensity +
", xdpi=" + xdpi + ", ydpi=" + ydpi + "}";
}
+
+ private static int getDeviceDensity() {
+ // qemu.sf.lcd_density can be used to override ro.sf.lcd_density
+ // when running in the emulator, allowing for dynamic configurations.
+ // The reason for this is that ro.sf.lcd_density is write-once and is
+ // set by the init process when it parses build.prop before anything else.
+ return SystemProperties.getInt("qemu.sf.lcd_density",
+ SystemProperties.getInt("ro.sf.lcd_density", DEFAULT_DENSITY));
+ }
}
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
new file mode 100644
index 0000000..d90045f
--- /dev/null
+++ b/core/java/android/util/LongSparseArray.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2009 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 android.util;
+
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * SparseArrays map longs to Objects. Unlike a normal array of Objects,
+ * there can be gaps in the indices. It is intended to be more efficient
+ * than using a HashMap to map Longs to Objects.
+ *
+ * @hide
+ */
+public class LongSparseArray<E> {
+ private static final Object DELETED = new Object();
+ private boolean mGarbage = false;
+
+ /**
+ * Creates a new SparseArray containing no mappings.
+ */
+ public LongSparseArray() {
+ this(10);
+ }
+
+ /**
+ * Creates a new SparseArray containing no mappings that will not
+ * require any additional memory allocation to store the specified
+ * number of mappings.
+ */
+ public LongSparseArray(int initialCapacity) {
+ initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
+
+ mKeys = new long[initialCapacity];
+ mValues = new Object[initialCapacity];
+ mSize = 0;
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or <code>null</code>
+ * if no such mapping has been made.
+ */
+ public E get(long key) {
+ return get(key, null);
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or the specified Object
+ * if no such mapping has been made.
+ */
+ public E get(long key, E valueIfKeyNotFound) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i < 0 || mValues[i] == DELETED) {
+ return valueIfKeyNotFound;
+ } else {
+ return (E) mValues[i];
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ public void delete(long key) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ if (mValues[i] != DELETED) {
+ mValues[i] = DELETED;
+ mGarbage = true;
+ }
+ }
+ }
+
+ /**
+ * Alias for {@link #delete(long)}.
+ */
+ public void remove(long key) {
+ delete(key);
+ }
+
+ private void gc() {
+ // Log.e("SparseArray", "gc start with " + mSize);
+
+ int n = mSize;
+ int o = 0;
+ long[] keys = mKeys;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ Object val = values[i];
+
+ if (val != DELETED) {
+ if (i != o) {
+ keys[o] = keys[i];
+ values[o] = val;
+ }
+
+ o++;
+ }
+ }
+
+ mGarbage = false;
+ mSize = o;
+
+ // Log.e("SparseArray", "gc end with " + mSize);
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(long key, E value) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] = value;
+ } else {
+ i = ~i;
+
+ if (i < mSize && mValues[i] == DELETED) {
+ mKeys[i] = key;
+ mValues[i] = value;
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+
+ // Search again because indices may have changed.
+ i = ~binarySearch(mKeys, 0, mSize, key);
+ }
+
+ if (mSize >= mKeys.length) {
+ int n = ArrayUtils.idealIntArraySize(mSize + 1);
+
+ long[] nkeys = new long[n];
+ Object[] nvalues = new Object[n];
+
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ if (mSize - i != 0) {
+ // Log.e("SparseArray", "move " + (mSize - i));
+ System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+ System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+ }
+
+ mKeys[i] = key;
+ mValues[i] = value;
+ mSize++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseArray
+ * currently stores.
+ */
+ public int size() {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mSize;
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the key from the <code>index</code>th key-value mapping that this
+ * SparseArray stores.
+ */
+ public long keyAt(int index) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mKeys[index];
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the value from the <code>index</code>th key-value mapping that this
+ * SparseArray stores.
+ */
+ public E valueAt(int index) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return (E) mValues[index];
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, sets a new
+ * value for the <code>index</code>th key-value mapping that this
+ * SparseArray stores.
+ */
+ public void setValueAt(int index, E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ mValues[index] = value;
+ }
+
+ /**
+ * Returns the index for which {@link #keyAt} would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ public int indexOfKey(long key) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return binarySearch(mKeys, 0, mSize, key);
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ public int indexOfValue(E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ for (int i = 0; i < mSize; i++)
+ if (mValues[i] == value)
+ return i;
+
+ return -1;
+ }
+
+ /**
+ * Removes all key-value mappings from this SparseArray.
+ */
+ public void clear() {
+ int n = mSize;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ values[i] = null;
+ }
+
+ mSize = 0;
+ mGarbage = false;
+ }
+
+ /**
+ * Puts a key/value pair into the array, optimizing for the case where
+ * the key is greater than all existing keys in the array.
+ */
+ public void append(long key, E value) {
+ if (mSize != 0 && key <= mKeys[mSize - 1]) {
+ put(key, value);
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+ }
+
+ int pos = mSize;
+ if (pos >= mKeys.length) {
+ int n = ArrayUtils.idealIntArraySize(pos + 1);
+
+ long[] nkeys = new long[n];
+ Object[] nvalues = new Object[n];
+
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ mKeys[pos] = key;
+ mValues[pos] = value;
+ mSize = pos + 1;
+ }
+
+ private static int binarySearch(long[] a, int start, int len, long key) {
+ int high = start + len, low = start - 1, guess;
+
+ while (high - low > 1) {
+ guess = (high + low) / 2;
+
+ if (a[guess] < key)
+ low = guess;
+ else
+ high = guess;
+ }
+
+ if (high == start + len)
+ return ~(start + len);
+ else if (a[high] == key)
+ return high;
+ else
+ return ~high;
+ }
+
+ private void checkIntegrity() {
+ for (int i = 1; i < mSize; i++) {
+ if (mKeys[i] <= mKeys[i - 1]) {
+ for (int j = 0; j < mSize; j++) {
+ Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]);
+ }
+
+ throw new RuntimeException();
+ }
+ }
+ }
+
+ private long[] mKeys;
+ private Object[] mValues;
+ private int mSize;
+}
\ No newline at end of file
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index 7739300..7542e88 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -18,6 +18,7 @@
import android.os.Handler;
import android.os.Message;
+import android.security.Keystore;
import android.util.Log;
final class JWebCoreJavaBridge extends Handler {
@@ -187,18 +188,11 @@
}
private String[] getKeyStrengthList() {
- // FIXME: fake the list for now
- String[] list = new String[2];
- list[0] = "1024";
- list[1] = "512";
- return list;
+ return Keystore.getInstance().getSupportedKeyStrenghs();
}
private String getSignedPublicKey(int index, String challenge, String url) {
- // FIXME: do nothing for now
- Log.w(LOGTAG, "getSignedPublicKey for " + index + " and challenge="
- + challenge + " and url=" + url);
- return "";
+ return Keystore.getInstance().generateKeyPair(index, challenge, url);
}
private native void nativeConstructor();
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 585ce3d..8f1b0ee 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -633,15 +633,19 @@
mDropDownList.getAdapter().getCount() - 1)) {
// When the selection is at the top, we block the key
// event to prevent focus from moving.
- mDropDownList.hideSelector();
- mDropDownList.requestLayout();
+ clearListSelection();
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
mPopup.update();
return true;
+ } else {
+ // WARNING: Please read the comment where mListSelectionHidden
+ // is declared
+ mDropDownList.mListSelectionHidden = false;
}
+
consumed = mDropDownList.onKeyDown(keyCode, event);
- if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed="
- + consumed);
+ if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed);
+
if (consumed) {
// If it handled the key event, then the user is
// navigating in the list, so we should put it in front.
@@ -784,9 +788,12 @@
* it back.
*/
public void clearListSelection() {
- if (mDropDownList != null) {
- mDropDownList.hideSelector();
- mDropDownList.requestLayout();
+ final DropDownListView list = mDropDownList;
+ if (list != null) {
+ // WARNING: Please read the comment where mListSelectionHidden is declared
+ list.mListSelectionHidden = true;
+ list.hideSelector();
+ list.requestLayout();
}
}
@@ -1079,8 +1086,7 @@
mPopup.showAsDropDown(getDropDownAnchorView(),
mDropDownHorizontalOffset, mDropDownVerticalOffset);
mDropDownList.setSelection(ListView.INVALID_POSITION);
- mDropDownList.hideSelector();
- mDropDownList.requestFocus();
+ clearListSelection();
post(mHideSelector);
}
}
@@ -1123,6 +1129,18 @@
mDropDownList.setOnItemClickListener(mDropDownItemClickListener);
mDropDownList.setFocusable(true);
mDropDownList.setFocusableInTouchMode(true);
+ mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ public void onItemSelected(AdapterView<?> parent, View view,
+ int position, long id) {
+
+ if (position != -1) {
+ mDropDownList.mListSelectionHidden = false;
+ }
+ }
+
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
if (mItemSelectedListener != null) {
mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
@@ -1246,10 +1264,7 @@
private class ListSelectorHider implements Runnable {
public void run() {
- if (mDropDownList != null) {
- mDropDownList.hideSelector();
- mDropDownList.requestLayout();
- }
+ clearListSelection();
}
}
@@ -1276,6 +1291,36 @@
* passed to the drop down; the list only looks focused.</p>
*/
private static class DropDownListView extends ListView {
+ /*
+ * WARNING: This is a workaround for a touch mode issue.
+ *
+ * Touch mode is propagated lazily to windows. This causes problems in
+ * the following scenario:
+ * - Type something in the AutoCompleteTextView and get some results
+ * - Move down with the d-pad to select an item in the list
+ * - Move up with the d-pad until the selection disappears
+ * - Type more text in the AutoCompleteTextView *using the soft keyboard*
+ * and get new results; you are now in touch mode
+ * - The selection comes back on the first item in the list, even though
+ * the list is supposed to be in touch mode
+ *
+ * Using the soft keyboard triggers the touch mode change but that change
+ * is propagated to our window only after the first list layout, therefore
+ * after the list attempts to resurrect the selection.
+ *
+ * The trick to work around this issue is to pretend the list is in touch
+ * mode when we know that the selection should not appear, that is when
+ * we know the user moved the selection away from the list.
+ *
+ * This boolean is set to true whenever we explicitely hide the list's
+ * selection and reset to false whenver we know the user moved the
+ * selection back to the list.
+ *
+ * When this boolean is true, isInTouchMode() returns true, otherwise it
+ * returns super.isInTouchMode().
+ */
+ private boolean mListSelectionHidden;
+
/**
* <p>Creates a new list view wrapper.</p>
*
@@ -1321,6 +1366,12 @@
return mSelectionBottomPadding;
}
+ @Override
+ public boolean isInTouchMode() {
+ // WARNING: Please read the comment where mListSelectionHidden is declared
+ return mListSelectionHidden || super.isInTouchMode();
+ }
+
/**
* <p>Returns the focus state in the drop down.</p>
*
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index a195ac7..6532125 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -174,6 +174,8 @@
setDividerHeight(dividerHeight);
}
+ setChoiceMode(a.getInt(R.styleable.ListView_choiceMode, CHOICE_MODE_NONE));
+
mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true);
mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true);
diff --git a/core/java/com/android/internal/backup/GoogleTransport.java b/core/java/com/android/internal/backup/GoogleTransport.java
deleted file mode 100644
index c089c23..0000000
--- a/core/java/com/android/internal/backup/GoogleTransport.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.android.internal.backup;
-
-import android.backup.RestoreSet;
-import android.content.pm.PackageInfo;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-/**
- * Backup transport for saving data to Google cloud storage.
- */
-
-public class GoogleTransport extends IBackupTransport.Stub {
-
- public long requestBackupTime() throws RemoteException {
- return 0; // !!! TODO: implement real backoff policy
- }
-
- public int startSession() throws RemoteException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- public int endSession() throws RemoteException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
- throws RemoteException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- // Restore handling
- public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
- // !!! TODO: real implementation
- return null;
- }
-
- public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
- // !!! TODO: real implementation
- return new PackageInfo[0];
- }
-
- public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor data)
- throws android.os.RemoteException {
- // !!! TODO: real implementation
- return 0;
- }
-}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 123c072..3ef8666 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -12,6 +12,8 @@
import android.os.RemoteException;
import android.util.Log;
+import org.bouncycastle.util.encoders.Base64;
+
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
@@ -78,25 +80,35 @@
byte[] buf = new byte[bufSize];
while (changeSet.readNextHeader()) {
String key = changeSet.getKey();
- int dataSize = changeSet.getDataSize();
- if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize);
- if (dataSize > bufSize) {
- bufSize = dataSize;
- buf = new byte[bufSize];
- }
- changeSet.readEntityData(buf, 0, dataSize);
- if (DEBUG) Log.v(TAG, " + data size " + dataSize);
+ String base64Key = new String(Base64.encode(key.getBytes()));
+ File entityFile = new File(packageDir, base64Key);
- File entityFile = new File(packageDir, key);
- FileOutputStream entity = new FileOutputStream(entityFile);
- try {
- entity.write(buf, 0, dataSize);
- } catch (IOException e) {
- Log.e(TAG, "Unable to update key file "
- + entityFile.getAbsolutePath());
- err = -1;
- } finally {
- entity.close();
+ int dataSize = changeSet.getDataSize();
+
+ if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize
+ + " key64=" + base64Key);
+
+ if (dataSize >= 0) {
+ FileOutputStream entity = new FileOutputStream(entityFile);
+
+ if (dataSize > bufSize) {
+ bufSize = dataSize;
+ buf = new byte[bufSize];
+ }
+ changeSet.readEntityData(buf, 0, dataSize);
+ if (DEBUG) Log.v(TAG, " data size " + dataSize);
+
+ try {
+ entity.write(buf, 0, dataSize);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to update key file "
+ + entityFile.getAbsolutePath());
+ err = -1;
+ } finally {
+ entity.close();
+ }
+ } else {
+ entityFile.delete();
}
}
} catch (IOException e) {
@@ -160,14 +172,15 @@
File[] blobs = packageDir.listFiles();
int err = 0;
if (blobs != null && blobs.length > 0) {
- BackupDataOutput out = new BackupDataOutput(mContext, outFd.getFileDescriptor());
+ BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
try {
for (File f : blobs) {
FileInputStream in = new FileInputStream(f);
int size = (int) f.length();
byte[] buf = new byte[size];
in.read(buf);
- out.writeEntityHeader(f.getName(), size);
+ String key = new String(Base64.decode(f.getName()));
+ out.writeEntityHeader(key, size);
out.writeEntityData(buf, size);
}
} catch (Exception e) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index fee8396..faa04f7 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -119,7 +119,8 @@
com_android_internal_graphics_NativeUtils.cpp \
android_backup_BackupDataInput.cpp \
android_backup_BackupDataOutput.cpp \
- android_backup_FileBackupHelper.cpp
+ android_backup_FileBackupHelper.cpp \
+ android_backup_RestoreHelperBase.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6d829fc..18f6d5f9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -158,6 +158,7 @@
extern int register_android_backup_BackupDataInput(JNIEnv *env);
extern int register_android_backup_BackupDataOutput(JNIEnv *env);
extern int register_android_backup_FileBackupHelper(JNIEnv *env);
+extern int register_android_backup_RestoreHelperBase(JNIEnv *env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1131,6 +1132,7 @@
REG_JNI(register_android_backup_BackupDataInput),
REG_JNI(register_android_backup_BackupDataOutput),
REG_JNI(register_android_backup_FileBackupHelper),
+ REG_JNI(register_android_backup_RestoreHelperBase),
};
/*
diff --git a/core/jni/android_backup_RestoreHelperBase.cpp b/core/jni/android_backup_RestoreHelperBase.cpp
new file mode 100644
index 0000000..3173420
--- /dev/null
+++ b/core/jni/android_backup_RestoreHelperBase.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#define LOG_TAG "FileBackupHelper_native"
+#include <utils/Log.h>
+
+#include "JNIHelp.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include <utils/BackupHelpers.h>
+
+namespace android
+{
+
+// java.io.FileDescriptor
+static jfieldID s_descriptorField = 0;
+
+static int
+ctor(JNIEnv* env, jobject clazz)
+{
+ return (int)new RestoreHelperBase();
+}
+
+static void
+dtor(JNIEnv* env, jobject clazz, jint ptr)
+{
+ delete (RestoreHelperBase*)ptr;
+}
+
+static int
+writeFile_native(JNIEnv* env, jobject clazz, jint ptr, jstring filenameObj, int backupReaderPtr)
+{
+ int err;
+ RestoreHelperBase* restore = (RestoreHelperBase*)ptr;
+ BackupDataReader* reader = (BackupDataReader*)backupReaderPtr;
+ char const* filename;
+
+ filename = env->GetStringUTFChars(filenameObj, NULL);
+
+ err = restore->WriteFile(String8(filename), reader);
+
+ env->ReleaseStringUTFChars(filenameObj, filename);
+
+ return err;
+}
+
+static int
+writeSnapshot_native(JNIEnv* env, jobject clazz, jint ptr, jobject fileDescriptor)
+{
+ int err;
+
+ RestoreHelperBase* restore = (RestoreHelperBase*)ptr;
+ int fd = env->GetIntField(fileDescriptor, s_descriptorField);
+
+ err = restore->WriteSnapshot(fd);
+
+ return err;
+}
+
+static const JNINativeMethod g_methods[] = {
+ { "ctor", "()I", (void*)ctor },
+ { "dtor", "(I)V", (void*)dtor },
+ { "writeFile_native", "(ILjava/lang/String;I)I", (void*)writeFile_native },
+ { "writeSnapshot_native", "(ILjava/io/FileDescriptor;)I", (void*)writeSnapshot_native },
+};
+
+int register_android_backup_RestoreHelperBase(JNIEnv* env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass("java/io/FileDescriptor");
+ LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
+ s_descriptorField = env->GetFieldID(clazz, "descriptor", "I");
+ LOG_FATAL_IF(s_descriptorField == NULL,
+ "Unable to find descriptor field in java.io.FileDescriptor");
+
+ return AndroidRuntime::registerNativeMethods(env, "android/backup/RestoreHelperBase",
+ g_methods, NELEM(g_methods));
+}
+
+}
diff --git a/core/jni/android_bluetooth_Database.cpp b/core/jni/android_bluetooth_Database.cpp
index 136c9a3..73b8efd 100644
--- a/core/jni/android_bluetooth_Database.cpp
+++ b/core/jni/android_bluetooth_Database.cpp
@@ -53,6 +53,7 @@
LOGE("Could not get onto the system bus!");
dbus_error_free(&err);
}
+ dbus_connection_set_exit_on_disconnect(conn, FALSE);
}
#endif
}
diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp
index fe94642..91a8e8e 100644
--- a/core/jni/android_server_BluetoothA2dpService.cpp
+++ b/core/jni/android_server_BluetoothA2dpService.cpp
@@ -84,6 +84,7 @@
dbus_error_free(&err);
return false;
}
+ dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
#endif /*HAVE_BLUETOOTH*/
return true;
}
diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp
index b6e9798..58ae4f6 100644
--- a/core/jni/android_server_BluetoothDeviceService.cpp
+++ b/core/jni/android_server_BluetoothDeviceService.cpp
@@ -109,6 +109,7 @@
dbus_error_free(&err);
return false;
}
+ dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME;
#endif /*HAVE_BLUETOOTH*/
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index 7c5da5b..ad24136 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -132,6 +132,7 @@
LOGE("%s: Could not get onto the system bus!", __FUNCTION__);
dbus_error_free(&err);
}
+ dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
}
#endif
}
@@ -161,6 +162,19 @@
NULL, agent_event_filter, NULL, NULL, NULL, NULL
};
+static unsigned int unix_events_to_dbus_flags(short events) {
+ return (events & DBUS_WATCH_READABLE ? POLLIN : 0) |
+ (events & DBUS_WATCH_WRITABLE ? POLLOUT : 0) |
+ (events & DBUS_WATCH_ERROR ? POLLERR : 0) |
+ (events & DBUS_WATCH_HANGUP ? POLLHUP : 0);
+}
+
+static short dbus_flags_to_unix_events(unsigned int flags) {
+ return (flags & POLLIN ? DBUS_WATCH_READABLE : 0) |
+ (flags & POLLOUT ? DBUS_WATCH_WRITABLE : 0) |
+ (flags & POLLERR ? DBUS_WATCH_ERROR : 0) |
+ (flags & POLLHUP ? DBUS_WATCH_HANGUP : 0);
+}
static jboolean setUpEventLoop(native_data_t *nat) {
LOGV(__FUNCTION__);
@@ -384,8 +398,7 @@
read(nat->controlFdR, &newFD, sizeof(int));
read(nat->controlFdR, &flags, sizeof(unsigned int));
read(nat->controlFdR, &watch, sizeof(DBusWatch *));
- int events = (flags & DBUS_WATCH_READABLE ? POLLIN : 0)
- | (flags & DBUS_WATCH_WRITABLE ? POLLOUT : 0);
+ short events = dbus_flags_to_unix_events(flags);
for (int y = 0; y<nat->pollMemberCount; y++) {
if ((nat->pollData[y].fd == newFD) &&
@@ -429,8 +442,7 @@
read(nat->controlFdR, &removeFD, sizeof(int));
read(nat->controlFdR, &flags, sizeof(unsigned int));
- int events = (flags & DBUS_WATCH_READABLE ? POLLIN : 0)
- | (flags & DBUS_WATCH_WRITABLE ? POLLOUT : 0);
+ short events = dbus_flags_to_unix_events(flags);
for (int y = 0; y < nat->pollMemberCount; y++) {
if ((nat->pollData[y].fd == removeFD) &&
@@ -494,13 +506,12 @@
}
}
} else {
- int event = nat->pollData[i].revents;
- int flags = (event & POLLIN ? DBUS_WATCH_READABLE : 0) |
- (event & POLLOUT ? DBUS_WATCH_WRITABLE : 0);
- dbus_watch_handle(nat->watchData[i], event);
- nat->pollData[i].revents = 0;
- // can only do one - it may have caused a 'remove'
- break;
+ short events = nat->pollData[i].revents;
+ unsigned int flags = unix_events_to_dbus_flags(events);
+ dbus_watch_handle(nat->watchData[i], flags);
+ nat->pollData[i].revents = 0;
+ // can only do one - it may have caused a 'remove'
+ break;
}
}
while (dbus_connection_dispatch(nat->conn) ==
diff --git a/core/res/res/drawable/call_contact.png b/core/res/res/drawable/call_contact.png
new file mode 100644
index 0000000..1abeb5d
--- /dev/null
+++ b/core/res/res/drawable/call_contact.png
Binary files differ
diff --git a/core/res/res/drawable/create_contact.png b/core/res/res/drawable/create_contact.png
new file mode 100644
index 0000000..5c5718b
--- /dev/null
+++ b/core/res/res/drawable/create_contact.png
Binary files differ
diff --git a/core/res/res/drawable/search_dropdown_background_apps.9.png b/core/res/res/drawable/search_dropdown_background_apps.9.png
index 56b697d..804260a 100644
--- a/core/res/res/drawable/search_dropdown_background_apps.9.png
+++ b/core/res/res/drawable/search_dropdown_background_apps.9.png
Binary files differ
diff --git a/core/res/res/values-ar-rEG/donottranslate-cldr.xml b/core/res/res/values-ar-rEG/donottranslate-cldr.xml
index 4826a41..2c20ffc 100644
--- a/core/res/res/values-ar-rEG/donottranslate-cldr.xml
+++ b/core/res/res/values-ar-rEG/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s، %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s، %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s، %3$s %2$s - %6$s، %8$s %7$s، %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-bg-rBG/donottranslate-cldr.xml b/core/res/res/values-bg-rBG/donottranslate-cldr.xml
index 010b974..cda072a 100644
--- a/core/res/res/values-bg-rBG/donottranslate-cldr.xml
+++ b/core/res/res/values-bg-rBG/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s, %1$s - %8$s %7$s y, %6$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-ca-rES/donottranslate-cldr.xml b/core/res/res/values-ca-rES/donottranslate-cldr.xml
index 4eabba7..d5abeef 100644
--- a/core/res/res/values-ca-rES/donottranslate-cldr.xml
+++ b/core/res/res/values-ca-rES/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s de %2$s - %6$s %8$s de %7$s de %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-cs-rCZ/donottranslate-cldr.xml b/core/res/res/values-cs-rCZ/donottranslate-cldr.xml
index 0670080..2e348b8 100644
--- a/core/res/res/values-cs-rCZ/donottranslate-cldr.xml
+++ b/core/res/res/values-cs-rCZ/donottranslate-cldr.xml
@@ -108,9 +108,9 @@
<string name="month_day">%-e. %B</string>
<string name="month">%-B</string>
<string name="month_year">%Y %B</string>
- <string name="abbrev_month_day">%b %-e</string>
- <string name="abbrev_month">%-b</string>
- <string name="abbrev_month_year">%Y %b</string>
+ <string name="abbrev_month_day">%-e.%-m</string>
+ <string name="abbrev_month">%-B</string>
+ <string name="abbrev_month_year">%-B %Y</string>
<string name="time1_time2">%1$s - %2$s</string>
<string name="date1_date2">%2$s - %5$s</string>
<string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string>
@@ -121,26 +121,27 @@
<string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string>
<string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s</string>
<string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string>
- <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string>
- <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string>
+ <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string>
+ <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string>
<string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
- <string name="time_wday_date">%1$s %2$s %3$s</string>
- <string name="wday_date">%2$s %3$s</string>
+ <string name="time_wday_date">%1$s %2$s, %3$s</string>
+ <string name="wday_date">%2$s, %3$s</string>
<string name="time_wday">%1$s %2$s</string>
<string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string>
- <string name="same_year_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string>
+ <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string>
<string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
<string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
- <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string>
- <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string>
+ <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string>
+ <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string>
<string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
- <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s - %10$s %6$s, %9$s %7$s %8$s</string>
- <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s - %10$s %6$s, %9$s %7$s %8$s</string>
- <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %4$s %2$s %3$s - %6$s, %9$s %7$s %8$s</string>
+ <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string>
+ <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string>
+ <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string>
<string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string>
- <string name="same_month_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string>
+ <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string>
<string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string>
+ <string name="short_format_month">%B</string>
</resources>
diff --git a/core/res/res/values-cs/donottranslate-cldr.xml b/core/res/res/values-cs/donottranslate-cldr.xml
index 0670080..2e348b8 100644
--- a/core/res/res/values-cs/donottranslate-cldr.xml
+++ b/core/res/res/values-cs/donottranslate-cldr.xml
@@ -108,9 +108,9 @@
<string name="month_day">%-e. %B</string>
<string name="month">%-B</string>
<string name="month_year">%Y %B</string>
- <string name="abbrev_month_day">%b %-e</string>
- <string name="abbrev_month">%-b</string>
- <string name="abbrev_month_year">%Y %b</string>
+ <string name="abbrev_month_day">%-e.%-m</string>
+ <string name="abbrev_month">%-B</string>
+ <string name="abbrev_month_year">%-B %Y</string>
<string name="time1_time2">%1$s - %2$s</string>
<string name="date1_date2">%2$s - %5$s</string>
<string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string>
@@ -121,26 +121,27 @@
<string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string>
<string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s</string>
<string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string>
- <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string>
- <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string>
+ <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string>
+ <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string>
<string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string>
- <string name="time_wday_date">%1$s %2$s %3$s</string>
- <string name="wday_date">%2$s %3$s</string>
+ <string name="time_wday_date">%1$s %2$s, %3$s</string>
+ <string name="wday_date">%2$s, %3$s</string>
<string name="time_wday">%1$s %2$s</string>
<string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string>
- <string name="same_year_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string>
+ <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string>
<string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
<string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string>
- <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string>
- <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string>
+ <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string>
+ <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string>
<string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string>
- <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s - %10$s %6$s, %9$s %7$s %8$s</string>
- <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s - %10$s %6$s, %9$s %7$s %8$s</string>
- <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %4$s %2$s %3$s - %6$s, %9$s %7$s %8$s</string>
+ <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string>
+ <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string>
+ <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string>
<string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string>
- <string name="same_month_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string>
+ <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string>
<string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string>
+ <string name="short_format_month">%B</string>
</resources>
diff --git a/core/res/res/values-da-rDK/donottranslate-cldr.xml b/core/res/res/values-da-rDK/donottranslate-cldr.xml
index 4a2a656..8cef5b2 100644
--- a/core/res/res/values-da-rDK/donottranslate-cldr.xml
+++ b/core/res/res/values-da-rDK/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s den %3$s. %2$s - %6$s den %8$s. %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-de-rAT/donottranslate-cldr.xml b/core/res/res/values-de-rAT/donottranslate-cldr.xml
index e6112ba..559e1ee 100644
--- a/core/res/res/values-de-rAT/donottranslate-cldr.xml
+++ b/core/res/res/values-de-rAT/donottranslate-cldr.xml
@@ -110,4 +110,5 @@
<string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-de-rCH/donottranslate-cldr.xml b/core/res/res/values-de-rCH/donottranslate-cldr.xml
index b611c08..2ca6787 100644
--- a/core/res/res/values-de-rCH/donottranslate-cldr.xml
+++ b/core/res/res/values-de-rCH/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-de-rDE/donottranslate-cldr.xml b/core/res/res/values-de-rDE/donottranslate-cldr.xml
index b611c08..2ca6787 100644
--- a/core/res/res/values-de-rDE/donottranslate-cldr.xml
+++ b/core/res/res/values-de-rDE/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-de-rLI/donottranslate-cldr.xml b/core/res/res/values-de-rLI/donottranslate-cldr.xml
index b611c08..2ca6787 100644
--- a/core/res/res/values-de-rLI/donottranslate-cldr.xml
+++ b/core/res/res/values-de-rLI/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-de/donottranslate-cldr.xml b/core/res/res/values-de/donottranslate-cldr.xml
index b611c08..2ca6787 100644
--- a/core/res/res/values-de/donottranslate-cldr.xml
+++ b/core/res/res/values-de/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-el-rGR/donottranslate-cldr.xml b/core/res/res/values-el-rGR/donottranslate-cldr.xml
index f76281a..e8f02fb 100644
--- a/core/res/res/values-el-rGR/donottranslate-cldr.xml
+++ b/core/res/res/values-el-rGR/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-en-rAU/donottranslate-cldr.xml b/core/res/res/values-en-rAU/donottranslate-cldr.xml
index 223a22b..5d1a8f6 100644
--- a/core/res/res/values-en-rAU/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rAU/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-en-rCA/donottranslate-cldr.xml b/core/res/res/values-en-rCA/donottranslate-cldr.xml
index 32fa2b0..5d7300e 100644
--- a/core/res/res/values-en-rCA/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rCA/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%2$s %3$s - %7$s %8$s, %9$s</string>
<string name="same_month_mdy1_mdy2">%2$s %3$s-%8$s, %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s - %6$s, %7$s %8$s, %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-en-rGB/donottranslate-cldr.xml b/core/res/res/values-en-rGB/donottranslate-cldr.xml
index b90112f..b115c6e 100644
--- a/core/res/res/values-en-rGB/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rGB/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-en-rIE/donottranslate-cldr.xml b/core/res/res/values-en-rIE/donottranslate-cldr.xml
index 4143da5..15fc8e8 100644
--- a/core/res/res/values-en-rIE/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rIE/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-en-rIN/donottranslate-cldr.xml b/core/res/res/values-en-rIN/donottranslate-cldr.xml
index 6522d677..2507ee8 100644
--- a/core/res/res/values-en-rIN/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rIN/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-en-rNZ/donottranslate-cldr.xml b/core/res/res/values-en-rNZ/donottranslate-cldr.xml
index d29455a..07d4fe8 100644
--- a/core/res/res/values-en-rNZ/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rNZ/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-en-rSG/donottranslate-cldr.xml b/core/res/res/values-en-rSG/donottranslate-cldr.xml
index 56c58e2..286cc0e 100644
--- a/core/res/res/values-en-rSG/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rSG/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%2$s %3$s – %7$s %8$s, %9$s</string>
<string name="same_month_mdy1_mdy2">%2$s %3$s – %8$s, %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-en-rUS/donottranslate-cldr.xml b/core/res/res/values-en-rUS/donottranslate-cldr.xml
index 56c58e2..286cc0e 100644
--- a/core/res/res/values-en-rUS/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rUS/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%2$s %3$s – %7$s %8$s, %9$s</string>
<string name="same_month_mdy1_mdy2">%2$s %3$s – %8$s, %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-en-rZA/donottranslate-cldr.xml b/core/res/res/values-en-rZA/donottranslate-cldr.xml
index e1aac04..9e8681b 100644
--- a/core/res/res/values-en-rZA/donottranslate-cldr.xml
+++ b/core/res/res/values-en-rZA/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-es-rES/donottranslate-cldr.xml b/core/res/res/values-es-rES/donottranslate-cldr.xml
index b516291..c1dc58b 100644
--- a/core/res/res/values-es-rES/donottranslate-cldr.xml
+++ b/core/res/res/values-es-rES/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-es-rUS/donottranslate-cldr.xml b/core/res/res/values-es-rUS/donottranslate-cldr.xml
index 587a615..24923c3 100644
--- a/core/res/res/values-es-rUS/donottranslate-cldr.xml
+++ b/core/res/res/values-es-rUS/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s de %2$s al %8$s de %7$s de %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s de %2$s al %6$s %8$s de %7$s de %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-es/donottranslate-cldr.xml b/core/res/res/values-es/donottranslate-cldr.xml
index b516291..c1dc58b 100644
--- a/core/res/res/values-es/donottranslate-cldr.xml
+++ b/core/res/res/values-es/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-fi-rFI/donottranslate-cldr.xml b/core/res/res/values-fi-rFI/donottranslate-cldr.xml
index 40cda53..dd12e57 100644
--- a/core/res/res/values-fi-rFI/donottranslate-cldr.xml
+++ b/core/res/res/values-fi-rFI/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s. %2$s – %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s.–%8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s – %6$s %8$s. %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-fr-rBE/donottranslate-cldr.xml b/core/res/res/values-fr-rBE/donottranslate-cldr.xml
index 0795cc5..ea4ecf2 100644
--- a/core/res/res/values-fr-rBE/donottranslate-cldr.xml
+++ b/core/res/res/values-fr-rBE/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s au %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s au %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-fr-rCA/donottranslate-cldr.xml b/core/res/res/values-fr-rCA/donottranslate-cldr.xml
index 7802ee0..346b971 100644
--- a/core/res/res/values-fr-rCA/donottranslate-cldr.xml
+++ b/core/res/res/values-fr-rCA/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">du %3$s %2$s au %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">du %1$s %3$s %2$s au %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-fr-rCH/donottranslate-cldr.xml b/core/res/res/values-fr-rCH/donottranslate-cldr.xml
index bbda44a..0a9835f 100644
--- a/core/res/res/values-fr-rCH/donottranslate-cldr.xml
+++ b/core/res/res/values-fr-rCH/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s au %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s au %6$s, %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-fr-rFR/donottranslate-cldr.xml b/core/res/res/values-fr-rFR/donottranslate-cldr.xml
index 76d4141..f340e83 100644
--- a/core/res/res/values-fr-rFR/donottranslate-cldr.xml
+++ b/core/res/res/values-fr-rFR/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-fr/donottranslate-cldr.xml b/core/res/res/values-fr/donottranslate-cldr.xml
index 76d4141..f340e83 100644
--- a/core/res/res/values-fr/donottranslate-cldr.xml
+++ b/core/res/res/values-fr/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-he-rIL/donottranslate-cldr.xml b/core/res/res/values-he-rIL/donottranslate-cldr.xml
index 1c8a6f7..e3feb1e 100644
--- a/core/res/res/values-he-rIL/donottranslate-cldr.xml
+++ b/core/res/res/values-he-rIL/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-hi-rIN/donottranslate-cldr.xml b/core/res/res/values-hi-rIN/donottranslate-cldr.xml
index ba4ded7..03da515 100644
--- a/core/res/res/values-hi-rIN/donottranslate-cldr.xml
+++ b/core/res/res/values-hi-rIN/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%9$s-%2$s-%3$s – %7$s-%8$s</string>
<string name="same_month_mdy1_mdy2">%9$s-%2$s-%3$s – %8$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s-%2$s-%3$s – %6$s, yyyy-%7$s-%8$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-hu-rHU/donottranslate-cldr.xml b/core/res/res/values-hu-rHU/donottranslate-cldr.xml
index 8dcb426..b56f520 100644
--- a/core/res/res/values-hu-rHU/donottranslate-cldr.xml
+++ b/core/res/res/values-hu-rHU/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%9$s. %2$s %3$s. - %7$s %8$s.</string>
<string name="same_month_mdy1_mdy2">%9$s. %2$s %3$s-%8$s.</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%9$s. %2$s %3$s., %1$s - %7$s %8$s., %6$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-id-rID/donottranslate-cldr.xml b/core/res/res/values-id-rID/donottranslate-cldr.xml
index feac981..22860a7 100644
--- a/core/res/res/values-id-rID/donottranslate-cldr.xml
+++ b/core/res/res/values-id-rID/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%9$s-%2$s-%3$s – %7$s-%8$s</string>
<string name="same_month_mdy1_mdy2">%9$s-%2$s-%3$s – %8$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s-%2$s-%3$s – %6$s, yyyy-%7$s-%8$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-it-rCH/donottranslate-cldr.xml b/core/res/res/values-it-rCH/donottranslate-cldr.xml
index 6b76f8e..6d9b550 100644
--- a/core/res/res/values-it-rCH/donottranslate-cldr.xml
+++ b/core/res/res/values-it-rCH/donottranslate-cldr.xml
@@ -138,4 +138,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-it-rIT/donottranslate-cldr.xml b/core/res/res/values-it-rIT/donottranslate-cldr.xml
index 929f899..1bf7992 100644
--- a/core/res/res/values-it-rIT/donottranslate-cldr.xml
+++ b/core/res/res/values-it-rIT/donottranslate-cldr.xml
@@ -138,4 +138,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-it/donottranslate-cldr.xml b/core/res/res/values-it/donottranslate-cldr.xml
index 929f899..1bf7992 100644
--- a/core/res/res/values-it/donottranslate-cldr.xml
+++ b/core/res/res/values-it/donottranslate-cldr.xml
@@ -138,4 +138,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-ja-rJP/donottranslate-cldr.xml b/core/res/res/values-ja-rJP/donottranslate-cldr.xml
index ae07433..10f2f79 100644
--- a/core/res/res/values-ja-rJP/donottranslate-cldr.xml
+++ b/core/res/res/values-ja-rJP/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日~%7$s月%8$s日</string>
<string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日~%8$s日</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日(%1$s)~%7$s月%8$s日(%6$s)</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-ja/donottranslate-cldr.xml b/core/res/res/values-ja/donottranslate-cldr.xml
index ae07433..10f2f79 100644
--- a/core/res/res/values-ja/donottranslate-cldr.xml
+++ b/core/res/res/values-ja/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日~%7$s月%8$s日</string>
<string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日~%8$s日</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日(%1$s)~%7$s月%8$s日(%6$s)</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-ko-rKR/donottranslate-cldr.xml b/core/res/res/values-ko-rKR/donottranslate-cldr.xml
index 4ec1ba4..45ca90a 100644
--- a/core/res/res/values-ko-rKR/donottranslate-cldr.xml
+++ b/core/res/res/values-ko-rKR/donottranslate-cldr.xml
@@ -131,4 +131,5 @@
<string name="same_year_mdy1_mdy2">%9$s년 %2$s %3$s일 ~ %7$s월 %8$s일</string>
<string name="same_month_mdy1_mdy2">%9$s년 %2$s %3$s일~%8$s일</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%9$s년 %2$s %3$s일 %1$s ~ %7$s월 %8$s일 %6$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-ko/donottranslate-cldr.xml b/core/res/res/values-ko/donottranslate-cldr.xml
index 4ec1ba4..45ca90a 100644
--- a/core/res/res/values-ko/donottranslate-cldr.xml
+++ b/core/res/res/values-ko/donottranslate-cldr.xml
@@ -131,4 +131,5 @@
<string name="same_year_mdy1_mdy2">%9$s년 %2$s %3$s일 ~ %7$s월 %8$s일</string>
<string name="same_month_mdy1_mdy2">%9$s년 %2$s %3$s일~%8$s일</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%9$s년 %2$s %3$s일 %1$s ~ %7$s월 %8$s일 %6$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-lt-rLT/donottranslate-cldr.xml b/core/res/res/values-lt-rLT/donottranslate-cldr.xml
index 17f228d..636a180 100644
--- a/core/res/res/values-lt-rLT/donottranslate-cldr.xml
+++ b/core/res/res/values-lt-rLT/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%9$s m. %2$s %3$s d. - %7$s %8$s d.</string>
<string name="same_month_mdy1_mdy2">%9$s m. %2$s %3$s d.-%8$s d.</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%9$s m. %2$s %3$s d.,%1$s - %7$s %8$s d.,%6$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-lv-rLV/donottranslate-cldr.xml b/core/res/res/values-lv-rLV/donottranslate-cldr.xml
index e6c3b79..3dec1d2 100644
--- a/core/res/res/values-lv-rLV/donottranslate-cldr.xml
+++ b/core/res/res/values-lv-rLV/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%9$s. gada %3$s. %2$s - %8$s. %7$s</string>
<string name="same_month_mdy1_mdy2">%9$s. gada %3$s.-%8$s. %2$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s. gada %3$s. %2$s - %6$s, y. gada %8$s. %7$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-nb/donottranslate-cldr.xml b/core/res/res/values-nb/donottranslate-cldr.xml
index 17c9b24..ecf0111 100644
--- a/core/res/res/values-nb/donottranslate-cldr.xml
+++ b/core/res/res/values-nb/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s. %2$s–%8$s. %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s.–%8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s–%6$s %8$s. %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-nl-rBE/donottranslate-cldr.xml b/core/res/res/values-nl-rBE/donottranslate-cldr.xml
index 72a4694..5b4cbf7 100644
--- a/core/res/res/values-nl-rBE/donottranslate-cldr.xml
+++ b/core/res/res/values-nl-rBE/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-nl-rNL/donottranslate-cldr.xml b/core/res/res/values-nl-rNL/donottranslate-cldr.xml
index 549d816..b6231b6 100644
--- a/core/res/res/values-nl-rNL/donottranslate-cldr.xml
+++ b/core/res/res/values-nl-rNL/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-nl/donottranslate-cldr.xml b/core/res/res/values-nl/donottranslate-cldr.xml
index 549d816..b6231b6 100644
--- a/core/res/res/values-nl/donottranslate-cldr.xml
+++ b/core/res/res/values-nl/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-pl-rPL/donottranslate-cldr.xml b/core/res/res/values-pl-rPL/donottranslate-cldr.xml
index c4bed0d..4ad17bf 100644
--- a/core/res/res/values-pl-rPL/donottranslate-cldr.xml
+++ b/core/res/res/values-pl-rPL/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-pl/donottranslate-cldr.xml b/core/res/res/values-pl/donottranslate-cldr.xml
index c4bed0d..4ad17bf 100644
--- a/core/res/res/values-pl/donottranslate-cldr.xml
+++ b/core/res/res/values-pl/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-pt-rBR/donottranslate-cldr.xml b/core/res/res/values-pt-rBR/donottranslate-cldr.xml
index 7b3304c..4729055 100644
--- a/core/res/res/values-pt-rBR/donottranslate-cldr.xml
+++ b/core/res/res/values-pt-rBR/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/donottranslate-cldr.xml b/core/res/res/values-pt-rPT/donottranslate-cldr.xml
index b14dbcb..be0fdb7 100644
--- a/core/res/res/values-pt-rPT/donottranslate-cldr.xml
+++ b/core/res/res/values-pt-rPT/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-pt/donottranslate-cldr.xml b/core/res/res/values-pt/donottranslate-cldr.xml
index 7b3304c..4729055 100644
--- a/core/res/res/values-pt/donottranslate-cldr.xml
+++ b/core/res/res/values-pt/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-ro-rRO/donottranslate-cldr.xml b/core/res/res/values-ro-rRO/donottranslate-cldr.xml
index 3672c94..4622445 100644
--- a/core/res/res/values-ro-rRO/donottranslate-cldr.xml
+++ b/core/res/res/values-ro-rRO/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-ru-rRU/donottranslate-cldr.xml b/core/res/res/values-ru-rRU/donottranslate-cldr.xml
index 2b8c235..21c06ff 100644
--- a/core/res/res/values-ru-rRU/donottranslate-cldr.xml
+++ b/core/res/res/values-ru-rRU/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s г.</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-ru/donottranslate-cldr.xml b/core/res/res/values-ru/donottranslate-cldr.xml
index 2b8c235..21c06ff 100644
--- a/core/res/res/values-ru/donottranslate-cldr.xml
+++ b/core/res/res/values-ru/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s г.</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-sk-rSK/donottranslate-cldr.xml b/core/res/res/values-sk-rSK/donottranslate-cldr.xml
index ea2a5ab..2b3c6d9 100644
--- a/core/res/res/values-sk-rSK/donottranslate-cldr.xml
+++ b/core/res/res/values-sk-rSK/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s. - %8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-sl-rSI/donottranslate-cldr.xml b/core/res/res/values-sl-rSI/donottranslate-cldr.xml
index b9c30c9..2b2b9c3 100644
--- a/core/res/res/values-sl-rSI/donottranslate-cldr.xml
+++ b/core/res/res/values-sl-rSI/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s. %2$s. – %8$s. %7$s. %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s.–%8$s. %2$s. %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s., %3$s. %2$s. – %6$s., %8$s. %7$s. %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-sr-rRS/donottranslate-cldr.xml b/core/res/res/values-sr-rRS/donottranslate-cldr.xml
index f88de6b..55ca968 100644
--- a/core/res/res/values-sr-rRS/donottranslate-cldr.xml
+++ b/core/res/res/values-sr-rRS/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s.</string>
<string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s.</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s.</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-sv-rSE/donottranslate-cldr.xml b/core/res/res/values-sv-rSE/donottranslate-cldr.xml
index c846719..a6ffc9a 100644
--- a/core/res/res/values-sv-rSE/donottranslate-cldr.xml
+++ b/core/res/res/values-sv-rSE/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s–%8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s–%6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-th-rTH/donottranslate-cldr.xml b/core/res/res/values-th-rTH/donottranslate-cldr.xml
index 53cd4d7..b3c76a3 100644
--- a/core/res/res/values-th-rTH/donottranslate-cldr.xml
+++ b/core/res/res/values-th-rTH/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-tr-rTR/donottranslate-cldr.xml b/core/res/res/values-tr-rTR/donottranslate-cldr.xml
index 2475b21..d612305 100644
--- a/core/res/res/values-tr-rTR/donottranslate-cldr.xml
+++ b/core/res/res/values-tr-rTR/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s %1$s - %8$s %7$s y %6$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-uk-rUA/donottranslate-cldr.xml b/core/res/res/values-uk-rUA/donottranslate-cldr.xml
index 51dabd0..55d3983 100644
--- a/core/res/res/values-uk-rUA/donottranslate-cldr.xml
+++ b/core/res/res/values-uk-rUA/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string>
<string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s – %6$s, %8$s %7$s %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-vi-rVN/donottranslate-cldr.xml b/core/res/res/values-vi-rVN/donottranslate-cldr.xml
index 0d9eebc..71178cc8 100644
--- a/core/res/res/values-vi-rVN/donottranslate-cldr.xml
+++ b/core/res/res/values-vi-rVN/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">Ngày %3$s tháng %2$s - Ngày %8$s tháng %7$s năm %9$s</string>
<string name="same_month_mdy1_mdy2">Ngày %3$s tháng %2$s - Ngày %8$s tháng M năm %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, ngày %3$s %2$s - %6$s, ngày %8$s %7$s năm %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-zh-rCN/donottranslate-cldr.xml b/core/res/res/values-zh-rCN/donottranslate-cldr.xml
index 6685a7d..8c386a4 100644
--- a/core/res/res/values-zh-rCN/donottranslate-cldr.xml
+++ b/core/res/res/values-zh-rCN/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日至%7$s月%8$s日</string>
<string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日至%8$s日</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日%1$s至%7$s月%8$s日%6$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/donottranslate-cldr.xml b/core/res/res/values-zh-rTW/donottranslate-cldr.xml
index 6685a7d..8c386a4 100644
--- a/core/res/res/values-zh-rTW/donottranslate-cldr.xml
+++ b/core/res/res/values-zh-rTW/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日至%7$s月%8$s日</string>
<string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日至%8$s日</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日%1$s至%7$s月%8$s日%6$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/core/res/res/values/donottranslate-cldr.xml b/core/res/res/values/donottranslate-cldr.xml
index 56c58e2..286cc0e 100644
--- a/core/res/res/values/donottranslate-cldr.xml
+++ b/core/res/res/values/donottranslate-cldr.xml
@@ -143,4 +143,5 @@
<string name="same_year_mdy1_mdy2">%2$s %3$s – %7$s %8$s, %9$s</string>
<string name="same_month_mdy1_mdy2">%2$s %3$s – %8$s, %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string>
+ <string name="short_format_month">%b</string>
</resources>
diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h
index 3ca8ad2..c78b99a 100644
--- a/include/utils/BackupHelpers.h
+++ b/include/utils/BackupHelpers.h
@@ -19,6 +19,7 @@
#include <utils/Errors.h>
#include <utils/String8.h>
+#include <utils/KeyedVector.h>
namespace android {
@@ -32,6 +33,27 @@
int dataSize; // size of the data, not including the padding, -1 means delete
} entity_header_v1;
+struct SnapshotHeader {
+ int magic0;
+ int fileCount;
+ int magic1;
+ int totalSize;
+};
+
+struct FileState {
+ int modTime_sec;
+ int modTime_nsec;
+ int size;
+ int crc32;
+ int nameLen;
+};
+
+struct FileRec {
+ String8 file;
+ bool deleted;
+ FileState s;
+};
+
/**
* Writes the data.
@@ -78,7 +100,7 @@
bool HasEntities();
status_t ReadEntityHeader(String8* key, size_t* dataSize);
status_t SkipEntityData(); // must be called with the pointer at the begining of the data.
- status_t ReadEntityData(void* data, size_t size);
+ ssize_t ReadEntityData(void* data, size_t size);
private:
explicit BackupDataReader();
@@ -94,11 +116,25 @@
int type;
entity_header_v1 entity;
} m_header;
+ String8 m_key;
};
int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD,
char const* const* files, char const* const *keys, int fileCount);
+class RestoreHelperBase
+{
+public:
+ RestoreHelperBase();
+ ~RestoreHelperBase();
+
+ status_t WriteFile(const String8& filename, BackupDataReader* in);
+ status_t WriteSnapshot(int fd);
+
+private:
+ void* m_buf;
+ KeyedVector<String8,FileRec> m_files;
+};
#define TEST_BACKUP_HELPERS 1
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index f14d7e9..eec645e 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -189,9 +189,14 @@
char property[PROPERTY_VALUE_MAX];
- if (property_get("ro.sf.lcd_density", property, NULL) <= 0) {
- LOGW("ro.sf.lcd_density not defined, using 160 dpi by default.");
- strcpy(property, "160");
+ /* Read density from build-specific ro.sf.lcd_density property
+ * except if it is overriden by qemu.sf.lcd_density.
+ */
+ if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) {
+ if (property_get("ro.sf.lcd_density", property, NULL) <= 0) {
+ LOGW("ro.sf.lcd_density not defined, using 160 dpi by default.");
+ strcpy(property, "160");
+ }
}
mDensity = atoi(property) * (1.0f/160.0f);
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
index 16ff1e5..6a7f056 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/utils/BackupData.cpp
@@ -205,12 +205,17 @@
amt = read(m_fd, &m_header, sizeof(m_header));
*done = m_done = (amt == 0);
CHECK_SIZE(amt, sizeof(m_header));
+ m_pos += sizeof(m_header);
+ if (type) {
+ *type = m_header.type;
+ }
// validate and fix up the fields.
m_header.type = fromlel(m_header.type);
switch (m_header.type)
{
case BACKUP_HEADER_ENTITY_V1:
+ {
m_header.entity.keyLen = fromlel(m_header.entity.keyLen);
if (m_header.entity.keyLen <= 0) {
LOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos,
@@ -219,15 +224,27 @@
}
m_header.entity.dataSize = fromlel(m_header.entity.dataSize);
m_entityCount++;
+
+ // read the rest of the header (filename)
+ size_t size = m_header.entity.keyLen;
+ char* buf = m_key.lockBuffer(size);
+ if (buf == NULL) {
+ m_status = ENOMEM;
+ return m_status;
+ }
+ int amt = read(m_fd, buf, size+1);
+ CHECK_SIZE(amt, (int)size+1);
+ m_key.unlockBuffer(size);
+ m_pos += size+1;
+ SKIP_PADDING();
+ m_dataEndPos = m_pos + m_header.entity.dataSize;
+
break;
+ }
default:
LOGD("Chunk header at %d has invalid type: 0x%08x", (int)m_pos, (int)m_header.type);
m_status = EINVAL;
}
- m_pos += sizeof(m_header);
- if (type) {
- *type = m_header.type;
- }
return m_status;
}
@@ -247,20 +264,8 @@
if (m_header.type != BACKUP_HEADER_ENTITY_V1) {
return EINVAL;
}
- size_t size = m_header.entity.keyLen;
- char* buf = key->lockBuffer(size);
- if (key == NULL) {
- key->unlockBuffer();
- m_status = ENOMEM;
- return m_status;
- }
- int amt = read(m_fd, buf, size+1);
- CHECK_SIZE(amt, (int)size+1);
- key->unlockBuffer(size);
- m_pos += size+1;
+ *key = m_key;
*dataSize = m_header.entity.dataSize;
- SKIP_PADDING();
- m_dataEndPos = m_pos + *dataSize;
return NO_ERROR;
}
@@ -281,25 +286,29 @@
}
}
-status_t
+ssize_t
BackupDataReader::ReadEntityData(void* data, size_t size)
{
if (m_status != NO_ERROR) {
- return m_status;
+ return -1;
}
int remaining = m_dataEndPos - m_pos;
- if (size > remaining) {
- printf("size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n",
- size, m_pos, m_dataEndPos, remaining);
- size = remaining;
- }
+ //LOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n",
+ // size, m_pos, m_dataEndPos, remaining);
if (remaining <= 0) {
return 0;
}
+ if (size > remaining) {
+ size = remaining;
+ }
+ //LOGD(" reading %d bytes", size);
int amt = read(m_fd, data, size);
- CHECK_SIZE(amt, (int)size);
- m_pos += size;
- return NO_ERROR;
+ if (amt < 0) {
+ m_status = errno;
+ return -1;
+ }
+ m_pos += amt;
+ return amt;
}
status_t
diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp
index c1d5404..d65a457 100644
--- a/libs/utils/BackupHelpers.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -47,27 +47,6 @@
#define LOGP(x...) LOGD(x)
#endif
-struct SnapshotHeader {
- int magic0;
- int fileCount;
- int magic1;
- int totalSize;
-};
-
-struct FileState {
- int modTime_sec;
- int modTime_nsec;
- int size;
- int crc32;
- int nameLen;
-};
-
-struct FileRec {
- char const* file; // this object does not own this string
- bool deleted;
- FileState s;
-};
-
const static int ROUND_UP[4] = { 0, 3, 2, 1 };
static inline int
@@ -310,7 +289,8 @@
for (int i=0; i<fileCount; i++) {
String8 key(keys[i]);
FileRec r;
- char const* file = r.file = files[i];
+ char const* file = files[i];
+ r.file = file;
struct stat st;
err = stat(file, &st);
@@ -351,20 +331,20 @@
}
else if (cmp > 0) {
// file added
- LOGP("file added: %s", g.file);
- write_update_file(dataStream, q, g.file);
+ LOGP("file added: %s", g.file.string());
+ write_update_file(dataStream, q, g.file.string());
m++;
}
else {
// both files exist, check them
const FileState& f = oldSnapshot.valueAt(n);
- int fd = open(g.file, O_RDONLY);
+ int fd = open(g.file.string(), O_RDONLY);
if (fd < 0) {
// We can't open the file. Don't report it as a delete either. Let the
// server keep the old version. Maybe they'll be able to deal with it
// on restore.
- LOGP("Unable to open file %s - skipping", g.file);
+ LOGP("Unable to open file %s - skipping", g.file.string());
} else {
g.s.crc32 = compute_crc32(fd);
@@ -375,7 +355,7 @@
g.s.modTime_sec, g.s.modTime_nsec, g.s.size, g.s.crc32);
if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec
|| f.size != g.s.size || f.crc32 != g.s.crc32) {
- write_update_file(dataStream, fd, p, g.file);
+ write_update_file(dataStream, fd, p, g.file.string());
}
close(fd);
@@ -395,7 +375,7 @@
while (m<fileCount) {
const String8& q = newSnapshot.keyAt(m);
FileRec& g = newSnapshot.editValueAt(m);
- write_update_file(dataStream, q, g.file);
+ write_update_file(dataStream, q, g.file.string());
m++;
}
@@ -404,6 +384,86 @@
return 0;
}
+#define RESTORE_BUF_SIZE (8*1024)
+
+RestoreHelperBase::RestoreHelperBase()
+{
+ m_buf = malloc(RESTORE_BUF_SIZE);
+}
+
+RestoreHelperBase::~RestoreHelperBase()
+{
+ free(m_buf);
+}
+
+status_t
+RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in)
+{
+ ssize_t err;
+ size_t dataSize;
+ String8 key;
+ int fd;
+ void* buf = m_buf;
+ ssize_t amt;
+ int mode;
+ int crc;
+ struct stat st;
+ FileRec r;
+
+ err = in->ReadEntityHeader(&key, &dataSize);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ // TODO: World readable/writable for now.
+ mode = 0666;
+
+ // Write the file and compute the crc
+ crc = crc32(0L, Z_NULL, 0);
+ fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode);
+ if (fd == -1) {
+ LOGW("Could not open file %s -- %s", filename.string(), strerror(errno));
+ return errno;
+ }
+
+ while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) {
+ err = write(fd, buf, amt);
+ if (err != amt) {
+ close(fd);
+ LOGW("Error '%s' writing '%s'", strerror(errno), filename.string());
+ return errno;
+ }
+ crc = crc32(crc, (Bytef*)buf, amt);
+ }
+
+ close(fd);
+
+ // Record for the snapshot
+ err = stat(filename.string(), &st);
+ if (err != 0) {
+ LOGW("Error stating file that we just created %s", filename.string());
+ return errno;
+ }
+
+ r.file = filename;
+ r.deleted = false;
+ r.s.modTime_sec = st.st_mtime;
+ r.s.modTime_nsec = 0; // workaround sim breakage
+ //r.s.modTime_nsec = st.st_mtime_nsec;
+ r.s.size = st.st_size;
+ r.s.crc32 = crc;
+
+ m_files.add(key, r);
+
+ return NO_ERROR;
+}
+
+status_t
+RestoreHelperBase::WriteSnapshot(int fd)
+{
+ return write_snapshot_file(fd, m_files);;
+}
+
#if TEST_BACKUP_HELPERS
#define SCRATCH_DIR "/data/backup_helper_test/"
@@ -560,7 +620,6 @@
FileState states[4];
FileRec r;
r.deleted = false;
- r.file = NULL;
states[0].modTime_sec = 0xfedcba98;
states[0].modTime_nsec = 0xdeadbeef;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 6f430c4..18a69c2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -158,14 +158,18 @@
getContext().checkCallingOrSelfPermission(
android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Cannot write secure settings table");
-
+ throw new SecurityException(
+ String.format("Permission denial: writing to secure settings requires $1%s",
+ android.Manifest.permission.WRITE_SECURE_SETTINGS));
+
// TODO: Move gservices into its own provider so we don't need this nonsense.
} else if ("gservices".equals(args.table) &&
getContext().checkCallingOrSelfPermission(
android.Manifest.permission.WRITE_GSERVICES) !=
PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Cannot write gservices table");
+ throw new SecurityException(
+ String.format("Permission denial: writing to gservices settings requires $1%s",
+ android.Manifest.permission.WRITE_GSERVICES));
}
}
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index f7dd0e8..e9c4ab7 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -15,10 +15,8 @@
*/
package android.tts;
-import android.speech.tts.ITts.Stub;
-import android.speech.tts.ITtsCallback;
-
import android.app.Service;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -31,6 +29,9 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.preference.PreferenceManager;
+import android.speech.tts.ITts.Stub;
+import android.speech.tts.ITtsCallback;
+import android.speech.tts.TextToSpeech;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
@@ -101,7 +102,7 @@
private MediaPlayer mPlayer;
private TtsService mSelf;
- private SharedPreferences prefs;
+ private ContentResolver mResolver;
private final ReentrantLock speechQueueLock = new ReentrantLock();
private final ReentrantLock synthesizerLock = new ReentrantLock();
@@ -113,9 +114,7 @@
super.onCreate();
Log.i("TTS", "TTS starting");
- // TODO: Make this work when the settings are done in the main Settings
- // app.
- prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ mResolver = getContentResolver();
String soLibPath = "/system/lib/libttspico.so";
nativeSynth = new SynthProxy(soLibPath);
@@ -129,10 +128,7 @@
mSpeechQueue = new ArrayList<SpeechItem>();
mPlayer = null;
- // TODO set default settings
- //setLanguage(prefs.getString("lang_pref", "en-rUS"));
- setLanguage("eng", "USA", "");
- setSpeechRate(Integer.parseInt(prefs.getString("rate_pref", "140")));
+ setDefaultSettings();
}
@Override
@@ -147,26 +143,50 @@
mCallbacks.kill();
}
+
+ private void setDefaultSettings() {
+
+ // TODO handle default language
+ setLanguage("eng", "USA", "");
+
+ // speech rate
+ setSpeechRate(getDefaultRate());
+
+ // TODO handle default pitch
+ }
+
+
+ private boolean isDefaultEnforced() {
+ return (android.provider.Settings.Secure.getInt(mResolver,
+ android.provider.Settings.Secure.TTS_USE_DEFAULTS,
+ TextToSpeech.Engine.FALLBACK_TTS_USE_DEFAULTS)
+ == 1 );
+ }
+
+
+ private int getDefaultRate() {
+ return android.provider.Settings.Secure.getInt(mResolver,
+ android.provider.Settings.Secure.TTS_DEFAULT_RATE,
+ TextToSpeech.Engine.FALLBACK_TTS_DEFAULT_RATE);
+ }
+
+
private void setSpeechRate(int rate) {
- if (prefs.getBoolean("override_pref", false)) {
- // This is set to the default here so that the preview in the prefs
- // activity will show the change without a restart, even if apps are
- // not allowed to change the defaults.
- rate = Integer.parseInt(prefs.getString("rate_pref", "140"));
+ if (isDefaultEnforced()) {
+ nativeSynth.setSpeechRate(getDefaultRate());
+ } else {
+ nativeSynth.setSpeechRate(rate);
}
- nativeSynth.setSpeechRate(rate);
}
private void setLanguage(String lang, String country, String variant) {
- if (prefs.getBoolean("override_pref", false)) {
- // This is set to the default here so that the preview in the prefs
- // activity will show the change without a restart, even if apps are
- // not
- // allowed to change the defaults.
- lang = prefs.getString("lang_pref", "en-rUS");
- }
Log.v("TTS", "TtsService.setLanguage("+lang+", "+country+", "+variant+")");
- nativeSynth.setLanguage(lang, country, variant);
+ if (isDefaultEnforced()) {
+ nativeSynth.setLanguage(lang, country, variant);
+ } else {
+ // TODO handle default language
+ nativeSynth.setLanguage("eng", "USA", "");
+ }
}
/**
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index d842d34..fa88111 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -21,14 +21,17 @@
import android.app.IApplicationThread;
import android.app.IBackupAgent;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -48,9 +51,10 @@
import android.backup.RestoreSet;
import com.android.internal.backup.LocalTransport;
-import com.android.internal.backup.GoogleTransport;
import com.android.internal.backup.IBackupTransport;
+import com.android.server.PackageManagerBackupAgent;
+
import java.io.EOFException;
import java.io.File;
import java.io.FileDescriptor;
@@ -69,6 +73,7 @@
private static final String TAG = "BackupManagerService";
private static final boolean DEBUG = true;
+ // Default time to wait after data changes before we back up the data
private static final long COLLECTION_INTERVAL = 1000;
//private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
@@ -103,6 +108,9 @@
// Backups that we haven't started yet.
private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
= new HashMap<ApplicationInfo,BackupRequest>();
+ // Do we need to back up the package manager metadata on the next pass?
+ private boolean mDoPackageManager;
+ private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
// Backups that we have started. These are separate to prevent starvation
// if an app keeps re-enqueuing itself.
private ArrayList<BackupRequest> mBackupQueue;
@@ -122,7 +130,7 @@
// Current active transport & restore session
private int mTransportId;
- private IBackupTransport mTransport;
+ private IBackupTransport mLocalTransport, mGoogleTransport;
private RestoreSession mActiveRestoreSession;
private File mStateDir;
@@ -146,15 +154,26 @@
mJournalDir.mkdirs();
makeJournalLocked(); // okay because no other threads are running yet
- // Build our mapping of uid to backup client services
+ // Build our mapping of uid to backup client services. This implicitly
+ // schedules a backup pass on the Package Manager metadata the first
+ // time anything needs to be backed up.
synchronized (mBackupParticipants) {
addPackageParticipantsLocked(null);
}
- // Stand up our default transport
- //!!! TODO: default to cloud transport, not local
- mTransportId = BackupManager.TRANSPORT_LOCAL;
- mTransport = createTransport(mTransportId);
+ // Set up our transport options and initialize the default transport
+ // TODO: Have transports register themselves somehow?
+ // TODO: Don't create transports that we don't need to?
+ //mTransportId = BackupManager.TRANSPORT_LOCAL;
+ mTransportId = BackupManager.TRANSPORT_GOOGLE;
+ mLocalTransport = new LocalTransport(context); // This is actually pretty cheap
+ mGoogleTransport = null;
+
+ // Attach to the Google backup transport.
+ Intent intent = new Intent().setComponent(new ComponentName(
+ "com.google.android.backup",
+ "com.google.android.backup.BackupTransportService"));
+ context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE);
// Now that we know about valid backup participants, parse any
// leftover journal files and schedule a new backup pass
@@ -249,6 +268,19 @@
}
};
+ // ----- Track connection to GoogleBackupTransport service -----
+ ServiceConnection mGoogleConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG) Log.v(TAG, "Connected to Google transport");
+ mGoogleTransport = IBackupTransport.Stub.asInterface(service);
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) Log.v(TAG, "Disconnected from Google transport");
+ mGoogleTransport = null;
+ }
+ };
+
// ----- Run the actual backup process asynchronously -----
private class BackupHandler extends Handler {
@@ -256,6 +288,13 @@
switch (msg.what) {
case MSG_RUN_BACKUP:
+ {
+ IBackupTransport transport = getTransport(mTransportId);
+ if (transport == null) {
+ Log.v(TAG, "Backup requested but no transport available");
+ break;
+ }
+
// snapshot the pending-backup set and work on that
File oldJournal = mJournal;
synchronized (mQueueLock) {
@@ -288,8 +327,10 @@
// deleted. If we crash prior to that, the old journal is parsed
// at next boot and the journaled requests fulfilled.
}
- (new PerformBackupThread(mTransport, mBackupQueue, oldJournal)).run();
+
+ (new PerformBackupThread(transport, mBackupQueue, oldJournal)).start();
break;
+ }
case MSG_RUN_FULL_BACKUP:
break;
@@ -298,7 +339,7 @@
{
int token = msg.arg1;
IBackupTransport transport = (IBackupTransport)msg.obj;
- (new PerformRestoreThread(transport, token)).run();
+ (new PerformRestoreThread(transport, token)).start();
break;
}
}
@@ -332,12 +373,13 @@
mBackupParticipants.put(uid, set);
}
set.add(app);
+ backUpPackageManagerData();
}
}
}
- // Remove the given package's backup services from our known active set. If
- // 'packageName' is null, *all* backup services will be removed.
+ // Remove the given package's entry from our known active set. If
+ // 'packageName' is null, *all* participating apps will be removed.
void removePackageParticipantsLocked(String packageName) {
if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName);
List<ApplicationInfo> allApps = null;
@@ -375,6 +417,7 @@
for (ApplicationInfo entry: set) {
if (entry.packageName.equals(app.packageName)) {
set.remove(entry);
+ backUpPackageManagerData();
break;
}
}
@@ -387,6 +430,7 @@
// Returns the set of all applications that define an android:backupAgent attribute
private List<ApplicationInfo> allAgentApps() {
+ // !!! TODO: cache this and regenerate only when necessary
List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0);
int N = allApps.size();
if (N > 0) {
@@ -416,25 +460,35 @@
addPackageParticipantsLockedInner(packageName, allApps);
}
- // Instantiate the given transport
- private IBackupTransport createTransport(int transportID) {
- IBackupTransport transport = null;
+ private void backUpPackageManagerData() {
+ // No need to schedule a backup just for the metadata; just piggyback on
+ // the next actual data backup.
+ synchronized(this) {
+ mDoPackageManager = true;
+ }
+ }
+
+ // The queue lock should be held when scheduling a backup pass
+ private void scheduleBackupPassLocked(long timeFromNowMillis) {
+ mBackupHandler.removeMessages(MSG_RUN_BACKUP);
+ mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, timeFromNowMillis);
+ }
+
+ // Return the given transport
+ private IBackupTransport getTransport(int transportID) {
switch (transportID) {
case BackupManager.TRANSPORT_LOCAL:
- if (DEBUG) Log.v(TAG, "Initializing local transport");
- transport = new LocalTransport(mContext);
- break;
+ Log.v(TAG, "Supplying local transport");
+ return mLocalTransport;
case BackupManager.TRANSPORT_GOOGLE:
- if (DEBUG) Log.v(TAG, "Initializing Google transport");
- //!!! TODO: stand up the google backup transport for real here
- transport = new GoogleTransport();
- break;
+ Log.v(TAG, "Supplying Google transport");
+ return mGoogleTransport;
default:
Log.e(TAG, "Asked for unknown transport " + transportID);
+ return null;
}
- return transport;
}
// fire off a backup agent, blocking until it attaches or times out
@@ -533,7 +587,29 @@
return;
}
- // The transport is up and running; now run all the backups in our queue
+ // The transport is up and running. First, back up the package manager
+ // metadata if necessary
+ boolean doPackageManager;
+ synchronized (BackupManagerService.this) {
+ doPackageManager = mDoPackageManager;
+ mDoPackageManager = false;
+ }
+ if (doPackageManager) {
+ // The package manager doesn't have a proper <application> etc, but since
+ // it's running here in the system process we can just set up its agent
+ // directly and use a synthetic BackupRequest.
+ if (DEBUG) Log.i(TAG, "Running PM backup pass as well");
+
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+ mPackageManager, allAgentApps());
+ BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+ pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
+ processOneBackup(pmRequest,
+ IBackupAgent.Stub.asInterface(pmAgent.onBind()),
+ mTransport);
+ }
+
+ // Now run all the backups in our queue
doQueuedBackups(mTransport);
// Finally, tear down the transport
@@ -584,8 +660,15 @@
// Look up the package info & signatures. This is first so that if it
// throws an exception, there's no file setup yet that would need to
// be unraveled.
- PackageInfo packInfo = mPackageManager.getPackageInfo(packageName,
+ PackageInfo packInfo;
+ if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
+ // The metadata 'package' is synthetic
+ packInfo = new PackageInfo();
+ packInfo.packageName = packageName;
+ } else {
+ packInfo = mPackageManager.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
+ }
// !!! TODO: get the state file dir from the transport
File savedStateName = new File(mStateDir, packageName);
@@ -673,6 +756,38 @@
return null;
}
+ private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
+ // Allow unsigned apps, but not signed on one device and unsigned on the other
+ // !!! TODO: is this the right policy?
+ if ((storedSigs == null || storedSigs.length == 0)
+ && (deviceSigs == null || deviceSigs.length == 0)) {
+ return true;
+ }
+ if (storedSigs == null || deviceSigs == null) {
+ return false;
+ }
+
+ // !!! TODO: this demands that every stored signature match one
+ // that is present on device, and does not demand the converse.
+ // Is this this right policy?
+ int nStored = storedSigs.length;
+ int nDevice = deviceSigs.length;
+
+ for (int i=0; i < nStored; i++) {
+ boolean match = false;
+ for (int j=0; j < nDevice; j++) {
+ if (storedSigs[i].equals(deviceSigs[j])) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ return false;
+ }
+ }
+ return true;
+ }
+
class PerformRestoreThread extends Thread {
private IBackupTransport mTransport;
private int mToken;
@@ -717,6 +832,13 @@
// !!! TODO: pick out the set for this token
mImage = images[0];
+ // Pull the Package Manager metadata from the restore set first
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+ mPackageManager, allAgentApps());
+ PackageInfo pmApp = new PackageInfo();
+ pmApp.packageName = PACKAGE_MANAGER_SENTINEL;
+ processOneRestore(pmApp, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+
// build the set of apps we will attempt to restore
PackageInfo[] packages = mTransport.getAppSet(mImage.token);
HashSet<PackageInfo> appsToRestore = new HashSet<PackageInfo>();
@@ -724,7 +846,15 @@
// get the real PackageManager idea of the package
PackageInfo app = isRestorable(pkg);
if (app != null) {
- appsToRestore.add(app);
+ // Validate against the backed-up signature block, too
+ Signature[] storedSigs
+ = pmAgent.getRestoredSignatures(app.packageName);
+ // !!! TODO: check app version here as well
+ if (signaturesMatch(storedSigs, app.signatures)) {
+ appsToRestore.add(app);
+ } else {
+ Log.w(TAG, "Sig mismatch on restore of " + app.packageName);
+ }
}
}
@@ -895,8 +1025,7 @@
// Schedule a backup pass in a few minutes. As backup-eligible data
// keeps changing, continue to defer the backup pass until things
// settle down, to avoid extra overhead.
- mBackupHandler.removeMessages(MSG_RUN_BACKUP);
- mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL);
+ scheduleBackupPassLocked(COLLECTION_INTERVAL);
}
} else {
Log.w(TAG, "dataChanged but no participant pkg " + packageName);
@@ -922,14 +1051,14 @@
if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
synchronized (mQueueLock) {
- mBackupHandler.removeMessages(MSG_RUN_BACKUP);
- mBackupHandler.sendEmptyMessage(MSG_RUN_BACKUP);
+ scheduleBackupPassLocked(0);
}
}
// Report the currently active transport
public int getCurrentTransport() {
mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
+ Log.v(TAG, "getCurrentTransport() returning " + mTransportId);
return mTransportId;
}
@@ -937,14 +1066,9 @@
public int selectBackupTransport(int transportId) {
mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport");
- int prevTransport = -1;
- IBackupTransport newTransport = createTransport(transportId);
- if (newTransport != null) {
- // !!! TODO: a method on the old transport that says it's being deactivated?
- mTransport = newTransport;
- prevTransport = mTransportId;
- mTransportId = transportId;
- }
+ int prevTransport = mTransportId;
+ mTransportId = transportId;
+ Log.v(TAG, "selectBackupTransport() set " + mTransportId + " returning " + prevTransport);
return prevTransport;
}
@@ -1005,7 +1129,7 @@
RestoreSet[] mRestoreSets = null;
RestoreSession(int transportID) {
- mRestoreTransport = createTransport(transportID);
+ mRestoreTransport = getTransport(transportID);
}
// --- Binder interface ---
diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java
new file mode 100644
index 0000000..e51a0af
--- /dev/null
+++ b/services/java/com/android/server/EntropyService.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 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.server;
+
+import java.io.File;
+import java.io.IOException;
+
+import android.os.Binder;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * A service designed to load and periodically save "randomness"
+ * for the Linux kernel.
+ *
+ * <p>When a Linux system starts up, the entropy pool associated with
+ * {@code /dev/random} may be in a fairly predictable state. Applications which
+ * depend strongly on randomness may find {@code /dev/random} or
+ * {@code /dev/urandom} returning predictable data. In order to counteract
+ * this effect, it's helpful to carry the entropy pool information across
+ * shutdowns and startups.
+ *
+ * <p>This class was modeled after the script in
+ * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">man
+ * 4 random</a>.
+ *
+ * <p>TODO: Investigate attempting to write entropy data at shutdown time
+ * instead of periodically.
+ */
+public class EntropyService extends Binder {
+ private static final String ENTROPY_FILENAME = getSystemDir() + "/entropy.dat";
+ private static final String TAG = "EntropyService";
+ private static final int ENTROPY_WHAT = 1;
+ private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs
+ private static final String RANDOM_DEV = "/dev/urandom";
+
+ /**
+ * Handler that periodically updates the entropy on disk.
+ */
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what != ENTROPY_WHAT) {
+ Log.e(TAG, "Will not process invalid message");
+ return;
+ }
+ writeEntropy();
+ scheduleEntropyWriter();
+ }
+ };
+
+ public EntropyService() {
+ loadInitialEntropy();
+ writeEntropy();
+ scheduleEntropyWriter();
+ }
+
+ private void scheduleEntropyWriter() {
+ mHandler.removeMessages(ENTROPY_WHAT);
+ mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT, ENTROPY_WRITE_PERIOD);
+ }
+
+ private void loadInitialEntropy() {
+ try {
+ RandomBlock.fromFile(ENTROPY_FILENAME).toFile(RANDOM_DEV);
+ } catch (IOException e) {
+ Log.w(TAG, "unable to load initial entropy (first boot?)", e);
+ }
+ }
+
+ private void writeEntropy() {
+ try {
+ RandomBlock.fromFile(RANDOM_DEV).toFile(ENTROPY_FILENAME);
+ } catch (IOException e) {
+ Log.e(TAG, "unable to write entropy", e);
+ }
+ }
+
+ private static String getSystemDir() {
+ File dataDir = Environment.getDataDirectory();
+ File systemDir = new File(dataDir, "system");
+ systemDir.mkdirs();
+ return systemDir.toString();
+ }
+}
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
new file mode 100644
index 0000000..6bd32a0
--- /dev/null
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2009 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.server;
+
+import android.app.BackupAgent;
+import android.backup.BackupDataInput;
+import android.backup.BackupDataOutput;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+// !!!TODO: take this out
+import java.util.zip.CRC32;
+
+/**
+ * We back up the signatures of each package so that during a system restore,
+ * we can verify that the app whose data we think we have matches the app
+ * actually resident on the device.
+ *
+ * Since the Package Manager isn't a proper "application" we just provide a
+ * direct IBackupAgent implementation and hand-construct it at need.
+ */
+public class PackageManagerBackupAgent extends BackupAgent {
+ private static final String TAG = "PMBA";
+ private static final boolean DEBUG = true;
+
+ private List<ApplicationInfo> mAllApps;
+ private PackageManager mPackageManager;
+ private HashMap<String, Signature[]> mRestoredSignatures;
+
+ // We're constructed with the set of applications that are participating
+ // in backup. This set changes as apps are installed & removed.
+ PackageManagerBackupAgent(PackageManager packageMgr, List<ApplicationInfo> apps) {
+ mPackageManager = packageMgr;
+ mAllApps = apps;
+ mRestoredSignatures = null;
+ }
+
+ public Signature[] getRestoredSignatures(String packageName) {
+ if (mRestoredSignatures == null) {
+ return null;
+ }
+
+ return mRestoredSignatures.get(packageName);
+ }
+
+ // The backed up data is the signature block for each app, keyed by
+ // the package name.
+ public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) {
+ HashSet<String> existing = parseStateFile(oldState);
+
+ // For each app we have on device, see if we've backed it up yet. If not,
+ // write its signature block to the output, keyed on the package name.
+ for (ApplicationInfo app : mAllApps) {
+ String packName = app.packageName;
+ if (!existing.contains(packName)) {
+ // We haven't stored this app's signatures yet, so we do that now
+ try {
+ PackageInfo info = mPackageManager.getPackageInfo(packName,
+ PackageManager.GET_SIGNATURES);
+ // build a byte array out of the signature list
+ byte[] sigs = flattenSignatureArray(info.signatures);
+// !!! TODO: take out this debugging
+ if (DEBUG) {
+ CRC32 crc = new CRC32();
+ crc.update(sigs);
+ Log.i(TAG, "+ flat sig array for " + packName + " : "
+ + crc.getValue());
+ }
+ data.writeEntityHeader(packName, sigs.length);
+ data.writeEntityData(sigs, sigs.length);
+ } catch (NameNotFoundException e) {
+ // Weird; we just found it, and now are told it doesn't exist.
+ // Treat it as having been removed from the device.
+ existing.add(packName);
+ } catch (IOException e) {
+ // Real error writing data
+ Log.e(TAG, "Unable to write package backup data file!");
+ return;
+ }
+ } else {
+ // We've already backed up this app. Remove it from the set so
+ // we can tell at the end what has disappeared from the device.
+ if (!existing.remove(packName)) {
+ Log.d(TAG, "*** failed to remove " + packName + " from package set!");
+ }
+ }
+ }
+
+ // At this point, the only entries in 'existing' are apps that were
+ // mentioned in the saved state file, but appear to no longer be present
+ // on the device. Write a deletion entity for them.
+ for (String app : existing) {
+ try {
+ data.writeEntityHeader(app, -1);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to write package deletions!");
+ return;
+ }
+ }
+
+ // Finally, write the new state blob -- just the list of all apps we handled
+ writeStateFile(mAllApps, newState);
+ }
+
+ // "Restore" here is a misnomer. What we're really doing is reading back the
+ // set of app signatures associated with each backed-up app in this restore
+ // image. We'll use those later to determine what we can legitimately restore.
+ public void onRestore(BackupDataInput data, ParcelFileDescriptor newState)
+ throws IOException {
+ List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
+ HashMap<String, Signature[]> sigMap = new HashMap<String, Signature[]>();
+
+ while (data.readNextHeader()) {
+ int dataSize = data.getDataSize();
+ byte[] buf = new byte[dataSize];
+ data.readEntityData(buf, 0, dataSize);
+
+ Signature[] sigs = unflattenSignatureArray(buf);
+ String pkg = data.getKey();
+// !!! TODO: take out this debugging
+ if (DEBUG) {
+ CRC32 crc = new CRC32();
+ crc.update(buf);
+ Log.i(TAG, "- unflat sig array for " + pkg + " : "
+ + crc.getValue());
+ }
+
+ ApplicationInfo app = new ApplicationInfo();
+ app.packageName = pkg;
+ restoredApps.add(app);
+ sigMap.put(pkg, sigs);
+ }
+
+ mRestoredSignatures = sigMap;
+ }
+
+
+ // Util: convert an array of Signatures into a flattened byte buffer. The
+ // flattened format contains enough info to reconstruct the signature array.
+ private byte[] flattenSignatureArray(Signature[] allSigs) {
+ ByteArrayOutputStream outBuf = new ByteArrayOutputStream();
+ DataOutputStream out = new DataOutputStream(outBuf);
+
+ // build the set of subsidiary buffers
+ try {
+ // first the # of signatures in the array
+ out.writeInt(allSigs.length);
+
+ // then the signatures themselves, length + flattened buffer
+ for (Signature sig : allSigs) {
+ byte[] flat = sig.toByteArray();
+ out.writeInt(flat.length);
+ out.write(flat);
+ }
+ } catch (IOException e) {
+ // very strange; we're writing to memory here. abort.
+ return null;
+ }
+
+ return outBuf.toByteArray();
+ }
+
+ private Signature[] unflattenSignatureArray(byte[] buffer) {
+ ByteArrayInputStream inBufStream = new ByteArrayInputStream(buffer);
+ DataInputStream in = new DataInputStream(inBufStream);
+ Signature[] sigs = null;
+
+ try {
+ int num = in.readInt();
+ sigs = new Signature[num];
+ for (int i = 0; i < num; i++) {
+ int len = in.readInt();
+ byte[] flatSig = new byte[len];
+ in.read(flatSig);
+ sigs[i] = new Signature(flatSig);
+ }
+ } catch (EOFException e) {
+ // clean termination
+ if (sigs == null) {
+ Log.w(TAG, "Empty signature block found");
+ }
+ } catch (IOException e) {
+ Log.d(TAG, "Unable to unflatten sigs");
+ return null;
+ }
+
+ return sigs;
+ }
+
+ // Util: parse out an existing state file into a usable structure
+ private HashSet<String> parseStateFile(ParcelFileDescriptor stateFile) {
+ HashSet<String> set = new HashSet<String>();
+ // The state file is just the list of app names we have stored signatures for
+ FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
+ DataInputStream in = new DataInputStream(instream);
+
+ int bufSize = 256;
+ byte[] buf = new byte[bufSize];
+ try {
+ int nameSize = in.readInt();
+ if (bufSize < nameSize) {
+ bufSize = nameSize + 32;
+ buf = new byte[bufSize];
+ }
+ in.read(buf, 0, nameSize);
+ String pkg = new String(buf, 0, nameSize);
+ set.add(pkg);
+ } catch (EOFException eof) {
+ // safe; we're done
+ } catch (IOException e) {
+ // whoops, bad state file. abort.
+ Log.e(TAG, "Unable to read Package Manager state file");
+ return null;
+ }
+ return set;
+ }
+
+ // Util: write a set of names into a new state file
+ private void writeStateFile(List<ApplicationInfo> apps, ParcelFileDescriptor stateFile) {
+ FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
+ DataOutputStream out = new DataOutputStream(outstream);
+
+ for (ApplicationInfo app : apps) {
+ try {
+ byte[] pkgNameBuf = app.packageName.getBytes();
+ out.writeInt(pkgNameBuf.length);
+ out.write(pkgNameBuf);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to write package manager state file!");
+ return;
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 73478e4..f18d6e0 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -19,7 +19,6 @@
import com.android.internal.app.ResolverActivity;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
-import com.android.server.PackageManagerService.PreferredActivity;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -30,7 +29,6 @@
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -152,6 +150,7 @@
final Context mContext;
final boolean mFactoryTest;
+ final boolean mNoDexOpt;
final DisplayMetrics mMetrics;
final int mDefParseFlags;
final String[] mSeparateProcesses;
@@ -303,6 +302,7 @@
mContext = context;
mFactoryTest = factoryTest;
+ mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings();
mSettings.addSharedUserLP("android.uid.system",
@@ -372,6 +372,10 @@
startTime);
int scanMode = SCAN_MONITOR;
+ if (mNoDexOpt) {
+ Log.w(TAG, "Running ENG build: no pre-dexopt!");
+ scanMode |= SCAN_NO_DEX;
+ }
final HashSet<String> libFiles = new HashSet<String>();
@@ -993,10 +997,11 @@
if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledLP(a.info, flags)) {
ActivityInfo ainfo = PackageParser.generateActivityInfo(a, flags);
- if (ainfo != null && (flags & PackageManager.GET_EXPANDABLE) != 0) {
+ if (ainfo != null) {
ApplicationInfo appInfo = getApplicationInfo(component.getPackageName(),
- PackageManager.GET_EXPANDABLE | PackageManager.GET_SUPPORTS_DENSITIES);
- if (appInfo != null && !appInfo.expandable) {
+ PackageManager.GET_SUPPORTS_DENSITIES);
+ if (appInfo != null &&
+ (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) == 0) {
// Check if the screen size is same as what the application expect.
CompatibilityInfo info = new CompatibilityInfo(appInfo);
DisplayMetrics metrics = new DisplayMetrics();
@@ -1009,11 +1014,13 @@
// Don't allow an app that cannot expand to handle rotation.
ainfo.configChanges &= ~ ActivityInfo.CONFIG_ORIENTATION;
} else {
- appInfo.expandable = true;
+ appInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
}
if (DEBUG_SETTINGS) {
Log.d(TAG, "component=" + component +
- ", expandable:" + appInfo.expandable);
+ ", expandable:" +
+ ((appInfo.flags &
+ ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0));
}
}
}
@@ -1269,28 +1276,6 @@
return chooseBestActivity(intent, resolvedType, flags, query);
}
- public ResolveInfo resolveIntentForPackage(Intent intent, String resolvedType,
- int flags, String packageName) {
- ComponentName comp = intent.getComponent();
- if (comp != null) {
- // if this is an explicit intent, it must have the same the packageName
- if (packageName.equals(comp.getPackageName())) {
- return resolveIntent(intent, resolvedType, flags);
- }
- return null;
- } else {
- List<ResolveInfo> query = null;
- synchronized (mPackages) {
- PackageParser.Package pkg = mPackages.get(packageName);
- if (pkg != null) {
- query = (List<ResolveInfo>) mActivities.
- queryIntentForPackage(intent, resolvedType, flags, pkg.activities);
- }
- }
- return chooseBestActivity(intent, resolvedType, flags, query);
- }
- }
-
private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
int flags, List<ResolveInfo> query) {
if (query != null) {
@@ -1409,8 +1394,17 @@
}
synchronized (mPackages) {
- return (List<ResolveInfo>)mActivities.
- queryIntent(intent, resolvedType, flags);
+ String pkgName = intent.getPackage();
+ if (pkgName == null) {
+ return (List<ResolveInfo>)mActivities.queryIntent(intent,
+ resolvedType, flags);
+ }
+ PackageParser.Package pkg = mPackages.get(pkgName);
+ if (pkg != null) {
+ return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent,
+ resolvedType, flags, pkg.activities);
+ }
+ return null;
}
}
@@ -1577,9 +1571,30 @@
public List<ResolveInfo> queryIntentReceivers(Intent intent,
String resolvedType, int flags) {
+ ComponentName comp = intent.getComponent();
+ if (comp != null) {
+ List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
+ ActivityInfo ai = getReceiverInfo(comp, flags);
+ if (ai != null) {
+ ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = ai;
+ list.add(ri);
+ }
+ return list;
+ }
+
synchronized (mPackages) {
- return (List<ResolveInfo>)mReceivers.
- queryIntent(intent, resolvedType, flags);
+ String pkgName = intent.getPackage();
+ if (pkgName == null) {
+ return (List<ResolveInfo>)mReceivers.queryIntent(intent,
+ resolvedType, flags);
+ }
+ PackageParser.Package pkg = mPackages.get(pkgName);
+ if (pkg != null) {
+ return (List<ResolveInfo>) mReceivers.queryIntentForPackage(intent,
+ resolvedType, flags, pkg.receivers);
+ }
+ return null;
}
}
@@ -1612,7 +1627,17 @@
}
synchronized (mPackages) {
- return (List<ResolveInfo>)mServices.queryIntent(intent, resolvedType, flags);
+ String pkgName = intent.getPackage();
+ if (pkgName == null) {
+ return (List<ResolveInfo>)mServices.queryIntent(intent,
+ resolvedType, flags);
+ }
+ PackageParser.Package pkg = mPackages.get(pkgName);
+ if (pkg != null) {
+ return (List<ResolveInfo>)mServices.queryIntentForPackage(intent,
+ resolvedType, flags, pkg.services);
+ }
+ return null;
}
}
@@ -1925,7 +1950,56 @@
}
return true;
}
+
+ public boolean performDexOpt(String packageName) {
+ if (!mNoDexOpt) {
+ return false;
+ }
+ PackageParser.Package p;
+ synchronized (mPackages) {
+ p = mPackages.get(packageName);
+ if (p == null || p.mDidDexOpt) {
+ return false;
+ }
+ }
+ synchronized (mInstallLock) {
+ return performDexOptLI(p, false) == DEX_OPT_PERFORMED;
+ }
+ }
+
+ static final int DEX_OPT_SKIPPED = 0;
+ static final int DEX_OPT_PERFORMED = 1;
+ static final int DEX_OPT_FAILED = -1;
+
+ private int performDexOptLI(PackageParser.Package pkg, boolean forceDex) {
+ boolean performed = false;
+ if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
+ String path = pkg.mScanPath;
+ int ret = 0;
+ try {
+ if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) {
+ ret = mInstaller.dexopt(path, pkg.applicationInfo.uid,
+ !pkg.mForwardLocked);
+ pkg.mDidDexOpt = true;
+ performed = true;
+ }
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "Apk not found for dexopt: " + path);
+ ret = -1;
+ } catch (IOException e) {
+ Log.w(TAG, "Exception reading apk: " + path, e);
+ ret = -1;
+ }
+ if (ret < 0) {
+ //error from installer
+ return DEX_OPT_FAILED;
+ }
+ }
+
+ return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
+ }
+
private PackageParser.Package scanPackageLI(
File scanFile, File destCodeFile, File destResourceFile,
PackageParser.Package pkg, int parseFlags, int scanMode) {
@@ -2221,23 +2295,11 @@
}
}
- if ((scanMode&SCAN_NO_DEX) == 0
- && (pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
- int ret = 0;
- try {
- if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) {
- ret = mInstaller.dexopt(path, pkg.applicationInfo.uid,
- (scanMode&SCAN_FORWARD_LOCKED) == 0);
- }
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Apk not found for dexopt: " + path);
- ret = -1;
- } catch (IOException e) {
- Log.w(TAG, "Exception reading apk: " + path, e);
- ret = -1;
- }
- if (ret < 0) {
- //error from installer
+ pkg.mForwardLocked = (scanMode&SCAN_FORWARD_LOCKED) != 0;
+ pkg.mScanPath = path;
+
+ if ((scanMode&SCAN_NO_DEX) == 0) {
+ if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) {
mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
return null;
}
@@ -3106,6 +3168,27 @@
(flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);
}
+ public List queryIntentForPackage(Intent intent, String resolvedType, int flags,
+ ArrayList<PackageParser.Service> packageServices) {
+ if (packageServices == null) {
+ return null;
+ }
+ mFlags = flags;
+ final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0;
+ int N = packageServices.size();
+ ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut =
+ new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N);
+
+ ArrayList<PackageParser.ServiceIntentInfo> intentFilters;
+ for (int i = 0; i < N; ++i) {
+ intentFilters = packageServices.get(i).intents;
+ if (intentFilters != null && intentFilters.size() > 0) {
+ listCut.add(intentFilters);
+ }
+ }
+ return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut);
+ }
+
public final void addService(PackageParser.Service s) {
mServices.put(s.component, s);
if (SHOW_INFO || Config.LOGV) Log.v(
diff --git a/services/java/com/android/server/RandomBlock.java b/services/java/com/android/server/RandomBlock.java
new file mode 100644
index 0000000..4ac1c6e
--- /dev/null
+++ b/services/java/com/android/server/RandomBlock.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2009 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.server;
+
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.DataOutput;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+
+/**
+ * A 4k block of random {@code byte}s.
+ */
+class RandomBlock {
+
+ private static final String TAG = "RandomBlock";
+ private static final int BLOCK_SIZE = 4096;
+ private byte[] block = new byte[BLOCK_SIZE];
+
+ private RandomBlock() { }
+
+ static RandomBlock fromFile(String filename) throws IOException {
+ Log.v(TAG, "reading from file " + filename);
+ InputStream stream = null;
+ try {
+ stream = new FileInputStream(filename);
+ return fromStream(stream);
+ } finally {
+ close(stream);
+ }
+ }
+
+ private static RandomBlock fromStream(InputStream in) throws IOException {
+ RandomBlock retval = new RandomBlock();
+ int total = 0;
+ while(total < BLOCK_SIZE) {
+ int result = in.read(retval.block, total, BLOCK_SIZE - total);
+ if (result == -1) {
+ throw new EOFException();
+ }
+ total += result;
+ }
+ return retval;
+ }
+
+ void toFile(String filename) throws IOException {
+ Log.v(TAG, "writing to file " + filename);
+ RandomAccessFile out = null;
+ try {
+ out = new RandomAccessFile(filename, "rws");
+ toDataOut(out);
+ truncateIfPossible(out);
+ } finally {
+ close(out);
+ }
+ }
+
+ private static void truncateIfPossible(RandomAccessFile f) {
+ try {
+ f.setLength(BLOCK_SIZE);
+ } catch (IOException e) {
+ // ignore this exception. Sometimes, the file we're trying to
+ // write is a character device, such as /dev/urandom, and
+ // these character devices do not support setting the length.
+ }
+ }
+
+ private void toDataOut(DataOutput out) throws IOException {
+ out.write(block);
+ }
+
+ private static void close(Closeable c) {
+ try {
+ if (c == null) {
+ return;
+ }
+ c.close();
+ } catch (IOException e) {
+ Log.w(TAG, "IOException thrown while closing Closeable", e);
+ }
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f6c1525..3e4d5f9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -97,6 +97,9 @@
// Critical services...
try {
+ Log.i(TAG, "Starting Entropy Service.");
+ ServiceManager.addService("entropy", new EntropyService());
+
Log.i(TAG, "Starting Power Manager.");
power = new PowerManagerService();
ServiceManager.addService(Context.POWER_SERVICE, power);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 755f9c8..8cf1cc2 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -181,7 +181,7 @@
// The flags that are set for all calls we make to the package manager.
static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES
- | PackageManager.GET_SUPPORTS_DENSITIES | PackageManager.GET_EXPANDABLE;
+ | PackageManager.GET_SUPPORTS_DENSITIES;
private static final String SYSTEM_SECURE = "ro.secure";
@@ -809,6 +809,12 @@
*/
int[] mProcDeaths = new int[20];
+ /**
+ * This is set if we had to do a delayed dexopt of an app before launching
+ * it, to increasing the ANR timeouts in that case.
+ */
+ boolean mDidDexOpt;
+
String mDebugApp = null;
boolean mWaitForDebugger = false;
boolean mDebugTransient = false;
@@ -1007,6 +1013,12 @@
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
+ if (mDidDexOpt) {
+ mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG);
+ mHandler.sendMessageDelayed(nmsg, BROADCAST_TIMEOUT);
+ return;
+ }
broadcastTimeout();
} break;
case PAUSE_TIMEOUT_MSG: {
@@ -1017,9 +1029,16 @@
activityPaused(token, null, true);
} break;
case IDLE_TIMEOUT_MSG: {
- IBinder token = (IBinder)msg.obj;
+ if (mDidDexOpt) {
+ mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
+ nmsg.obj = msg.obj;
+ mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT);
+ return;
+ }
// We don't at this point know if the activity is fullscreen,
// so we need to be conservative and assume it isn't.
+ IBinder token = (IBinder)msg.obj;
Log.w(TAG, "Activity idle timeout for " + token);
activityIdleInternal(token, true);
} break;
@@ -1035,6 +1054,13 @@
activityIdle(token);
} break;
case SERVICE_TIMEOUT_MSG: {
+ if (mDidDexOpt) {
+ mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
+ nmsg.obj = msg.obj;
+ mHandler.sendMessageDelayed(nmsg, SERVICE_TIMEOUT);
+ return;
+ }
serviceTimeout((ProcessRecord)msg.obj);
} break;
case UPDATE_TIME_ZONE: {
@@ -1071,6 +1097,12 @@
}
} break;
case LAUNCH_TIMEOUT_MSG: {
+ if (mDidDexOpt) {
+ mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);
+ mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT);
+ return;
+ }
synchronized (ActivityManagerService.this) {
if (mLaunchingActivity.isHeld()) {
Log.w(TAG, "Launch timeout has expired, giving up wake lock!");
@@ -1091,6 +1123,13 @@
}
}
case PROC_START_TIMEOUT_MSG: {
+ if (mDidDexOpt) {
+ mDidDexOpt = false;
+ Message nmsg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
+ nmsg.obj = msg.obj;
+ mHandler.sendMessageDelayed(nmsg, PROC_START_TIMEOUT);
+ return;
+ }
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
processStartTimedOutLocked(app);
@@ -1607,6 +1646,16 @@
return proc;
}
+ private void ensurePackageDexOpt(String packageName) {
+ IPackageManager pm = ActivityThread.getPackageManager();
+ try {
+ if (pm.performDexOpt(packageName)) {
+ mDidDexOpt = true;
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
private boolean isNextTransitionForward() {
int transit = mWindowManager.getPendingAppTransition();
return transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN
@@ -1666,6 +1715,7 @@
if (r.isHomeActivity) {
mHomeProcess = app;
}
+ ensurePackageDexOpt(r.intent.getComponent().getPackageName());
app.thread.scheduleLaunchActivity(new Intent(r.intent), r,
r.info, r.icicle, results, newIntents, !andResume,
isNextTransitionForward());
@@ -4819,6 +4869,10 @@
isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
}
+ ensurePackageDexOpt(app.info.packageName);
+ if (app.instrumentationInfo != null) {
+ ensurePackageDexOpt(app.instrumentationInfo.packageName);
+ }
thread.bindApplication(processName, app.instrumentationInfo != null
? app.instrumentationInfo : app.info, providers,
app.instrumentationClass, app.instrumentationProfileFile,
@@ -4907,6 +4961,7 @@
// Check whether the next backup agent is in this process...
if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) {
if (DEBUG_BACKUP) Log.v(TAG, "New app is backup target, launching agent for " + app);
+ ensurePackageDexOpt(mBackupTarget.appInfo.packageName);
try {
thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode);
} catch (Exception e) {
@@ -6918,6 +6973,7 @@
}
app.pubProviders.put(cpi.name, cpr);
app.addPackage(cpi.applicationInfo.packageName);
+ ensurePackageDexOpt(cpi.applicationInfo.packageName);
}
}
return providers;
@@ -7904,7 +7960,8 @@
// is there an Activity in this package that handles ACTION_APP_ERROR?
Intent intent = new Intent(Intent.ACTION_APP_ERROR);
- ResolveInfo info = pm.resolveIntentForPackage(intent, null, 0, installerPackageName);
+ intent.setPackage(installerPackageName);
+ ResolveInfo info = pm.resolveIntent(intent, null, 0);
if (info == null || info.activityInfo == null) {
return null;
}
@@ -9541,6 +9598,7 @@
synchronized (r.stats.getBatteryStats()) {
r.stats.startLaunchedLocked();
}
+ ensurePackageDexOpt(r.serviceInfo.packageName);
app.thread.scheduleCreateService(r, r.serviceInfo);
created = true;
} finally {
@@ -11097,6 +11155,7 @@
if (DEBUG_BROADCAST_LIGHT) Log.v(TAG,
"Delivering to component " + r.curComponent
+ ": " + r);
+ ensurePackageDexOpt(r.intent.getComponent().getPackageName());
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
r.resultCode, r.resultData, r.resultExtras, r.ordered);
started = true;
diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java
index 944ea02..b3fc313 100644
--- a/services/java/com/android/server/am/HistoryRecord.java
+++ b/services/java/com/android/server/am/HistoryRecord.java
@@ -463,6 +463,12 @@
return false;
}
+ if (service.mDidDexOpt) {
+ // Give more time since we were dexopting.
+ service.mDidDexOpt = false;
+ return false;
+ }
+
if (r.app.instrumentationClass == null) {
service.appNotRespondingLocked(r.app, r, "keyDispatchingTimedOut");
} else {
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 866334b..2d58659 100755
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -56,9 +56,9 @@
private static final String TAG = "UsageStats";
// Current on-disk Parcel version
- private static final int VERSION = 1004;
+ private static final int VERSION = 1005;
- private static final int CHECKIN_VERSION = 3;
+ private static final int CHECKIN_VERSION = 4;
private static final String FILE_PREFIX = "usage-";
@@ -82,7 +82,9 @@
// this lock held.
final Object mFileLock;
// Order of locks is mFileLock followed by mStatsLock to avoid deadlocks
- private String mResumedPkg;
+ private String mLastResumedPkg;
+ private String mLastResumedComp;
+ private boolean mIsResumed;
private File mFile;
private String mFileLeaf;
//private File mBackupFile;
@@ -92,11 +94,16 @@
private int mLastWriteDay;
static class TimeStats {
+ int count;
int[] times = new int[NUM_LAUNCH_TIME_BINS];
TimeStats() {
}
+ void incCount() {
+ count++;
+ }
+
void add(int val) {
final int[] bins = LAUNCH_TIME_BINS;
for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
@@ -109,6 +116,7 @@
}
TimeStats(Parcel in) {
+ count = in.readInt();
final int[] localTimes = times;
for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
localTimes[i] = in.readInt();
@@ -116,6 +124,7 @@
}
void writeToParcel(Parcel out) {
+ out.writeInt(count);
final int[] localTimes = times;
for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
out.writeInt(localTimes[i]);
@@ -152,8 +161,10 @@
}
}
- void updateResume() {
- mLaunchCount ++;
+ void updateResume(boolean launched) {
+ if (launched) {
+ mLaunchCount ++;
+ }
mResumedTime = SystemClock.elapsedRealtime();
}
@@ -162,6 +173,15 @@
mUsageTime += (mPausedTime - mResumedTime);
}
+ void addLaunchCount(String comp) {
+ TimeStats times = mLaunchTimes.get(comp);
+ if (times == null) {
+ times = new TimeStats();
+ mLaunchTimes.put(comp, times);
+ }
+ times.incCount();
+ }
+
void addLaunchTime(String comp, int millis) {
TimeStats times = mLaunchTimes.get(comp);
if (times == null) {
@@ -436,43 +456,70 @@
public void noteResumeComponent(ComponentName componentName) {
enforceCallingPermission();
String pkgName;
- if ((componentName == null) ||
- ((pkgName = componentName.getPackageName()) == null)) {
- return;
- }
- if ((mResumedPkg != null) && (mResumedPkg.equalsIgnoreCase(pkgName))) {
- // Moving across activities in same package. just return
- return;
- }
- if (localLOGV) Log.i(TAG, "started component:"+pkgName);
synchronized (mStatsLock) {
+ if ((componentName == null) ||
+ ((pkgName = componentName.getPackageName()) == null)) {
+ return;
+ }
+
+ final boolean samePackage = pkgName.equals(mLastResumedPkg);
+ if (mIsResumed) {
+ if (samePackage) {
+ Log.w(TAG, "Something wrong here, didn't expect "
+ + pkgName + " to be resumed");
+ return;
+ }
+
+ if (mLastResumedPkg != null) {
+ // We last resumed some other package... just pause it now
+ // to recover.
+ Log.w(TAG, "Unexpected resume of " + pkgName
+ + " while already resumed in " + mLastResumedPkg);
+ PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
+ if (pus != null) {
+ pus.updatePause();
+ }
+ }
+ }
+
+ final boolean sameComp = samePackage
+ && componentName.getClassName().equals(mLastResumedComp);
+
+ mIsResumed = true;
+ mLastResumedPkg = pkgName;
+ mLastResumedComp = componentName.getClassName();
+
+ if (localLOGV) Log.i(TAG, "started component:" + pkgName);
PkgUsageStatsExtended pus = mStats.get(pkgName);
if (pus == null) {
pus = new PkgUsageStatsExtended();
mStats.put(pkgName, pus);
}
- pus.updateResume();
+ pus.updateResume(!samePackage);
+ if (!sameComp) {
+ pus.addLaunchCount(mLastResumedComp);
+ }
}
- mResumedPkg = pkgName;
}
public void notePauseComponent(ComponentName componentName) {
enforceCallingPermission();
- String pkgName;
- if ((componentName == null) ||
- ((pkgName = componentName.getPackageName()) == null)) {
- return;
- }
- if ((mResumedPkg == null) || (!pkgName.equalsIgnoreCase(mResumedPkg))) {
- Log.w(TAG, "Something wrong here, Didn't expect "+pkgName+" to be paused");
- return;
- }
- if (localLOGV) Log.i(TAG, "paused component:"+pkgName);
-
- // Persist current data to file if needed.
- writeStatsToFile(false);
synchronized (mStatsLock) {
+ String pkgName;
+ if ((componentName == null) ||
+ ((pkgName = componentName.getPackageName()) == null)) {
+ return;
+ }
+ if (!mIsResumed) {
+ Log.w(TAG, "Something wrong here, didn't expect "
+ + pkgName + " to be paused");
+ return;
+ }
+ mIsResumed = false;
+
+ if (localLOGV) Log.i(TAG, "paused component:"+pkgName);
+
PkgUsageStatsExtended pus = mStats.get(pkgName);
if (pus == null) {
// Weird some error here
@@ -481,6 +528,9 @@
}
pus.updatePause();
}
+
+ // Persist current data to file if needed.
+ writeStatsToFile(false);
}
public void noteLaunchTime(ComponentName componentName, int millis) {
@@ -631,9 +681,9 @@
if (isCompactOutput) {
sb.append("P:");
sb.append(pkgName);
- sb.append(",");
+ sb.append(',');
sb.append(pus.mLaunchCount);
- sb.append(",");
+ sb.append(',');
sb.append(pus.mUsageTime);
sb.append('\n');
final int NC = pus.mLaunchTimes.size();
@@ -642,6 +692,8 @@
sb.append("A:");
sb.append(ent.getKey());
TimeStats times = ent.getValue();
+ sb.append(',');
+ sb.append(times.count);
for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) {
sb.append(",");
sb.append(times.times[i]);
@@ -665,25 +717,26 @@
sb.append(" ");
sb.append(ent.getKey());
TimeStats times = ent.getValue();
+ sb.append(": ");
+ sb.append(times.count);
+ sb.append(" starts");
int lastBin = 0;
- boolean first = true;
for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) {
if (times.times[i] != 0) {
- sb.append(first ? ": " : ", ");
+ sb.append(", ");
sb.append(lastBin);
sb.append('-');
sb.append(LAUNCH_TIME_BINS[i]);
- sb.append('=');
+ sb.append("ms=");
sb.append(times.times[i]);
- first = false;
}
lastBin = LAUNCH_TIME_BINS[i];
}
if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) {
- sb.append(first ? ": " : ", ");
+ sb.append(", ");
sb.append(">=");
sb.append(lastBin);
- sb.append('=');
+ sb.append("ms=");
sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]);
}
sb.append('\n');
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 9395d66..890f930 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -22,7 +22,6 @@
import android.text.TextUtils;
import com.android.internal.telephony.EncodeException;
-import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.ISms;
import com.android.internal.telephony.IccConstants;
import com.android.internal.telephony.SmsRawData;
@@ -31,14 +30,12 @@
import java.util.Arrays;
import java.util.List;
-import static android.telephony.SmsMessage.ENCODING_7BIT;
-import static android.telephony.SmsMessage.ENCODING_8BIT;
-import static android.telephony.SmsMessage.ENCODING_16BIT;
-import static android.telephony.SmsMessage.ENCODING_UNKNOWN;
-import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES;
-import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER;
-import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS;
-import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER;
+/*
+ * TODO(code review): Curious question... Why are a lot of these
+ * methods not declared as static, since they do not seem to require
+ * any local object state? Assumedly this cannot be changed without
+ * interfering with the API...
+ */
/**
* Manages SMS operations such as sending data, text, and pdu SMS messages.
@@ -88,7 +85,7 @@
}
/**
- * Divide a text message into several messages, none bigger than
+ * Divide a message text into several fragments, none bigger than
* the maximum SMS message size.
*
* @param text the original message. Must not be null.
@@ -96,40 +93,7 @@
* comprise the original message
*/
public ArrayList<String> divideMessage(String text) {
- int size = text.length();
- int[] params = SmsMessage.calculateLength(text, false);
- /* SmsMessage.calculateLength returns an int[4] with:
- * int[0] being the number of SMS's required,
- * int[1] the number of code units used,
- * int[2] is the number of code units remaining until the next message.
- * int[3] is the encoding type that should be used for the message.
- */
- int messageCount = params[0];
- int encodingType = params[3];
- ArrayList<String> result = new ArrayList<String>(messageCount);
-
- int start = 0;
- int limit;
-
- if (messageCount > 1) {
- limit = (encodingType == ENCODING_7BIT)?
- MAX_USER_DATA_SEPTETS_WITH_HEADER: MAX_USER_DATA_BYTES_WITH_HEADER;
- } else {
- limit = (encodingType == ENCODING_7BIT)?
- MAX_USER_DATA_SEPTETS: MAX_USER_DATA_BYTES;
- }
-
- try {
- while (start < size) {
- int end = GsmAlphabet.findLimitIndex(text, start, limit, encodingType);
- result.add(text.substring(start, end));
- start = end;
- }
- }
- catch (EncodeException e) {
- // ignore it.
- }
- return result;
+ return SmsMessage.fragmentText(text);
}
/**
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index b60da5a..775b034 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -17,12 +17,17 @@
package android.telephony;
import android.os.Parcel;
+import android.util.Log;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.EncodeException;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
+import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
+
+import java.lang.Math;
+import java.util.ArrayList;
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
@@ -44,19 +49,41 @@
UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
}
- /** Unknown encoding scheme (see TS 23.038) */
+ /**
+ * TODO(cleanup): given that we now have more than one possible
+ * 7bit encoding, this result starts to look rather vague and
+ * maybe confusing... If this is just an indication of code unit
+ * size, maybe that is no problem. Otherwise, should we try to
+ * create an aggregate collection of GSM and CDMA encodings? CDMA
+ * contains a superset of the encodings we use (it does not
+ * support 8-bit GSM, but we also do not use that encoding
+ * currently)... We could get rid of these and directly reference
+ * the CDMA encoding definitions...
+ */
+
+ /** User data text encoding code unit size */
public static final int ENCODING_UNKNOWN = 0;
- /** 7-bit encoding scheme (see TS 23.038) */
public static final int ENCODING_7BIT = 1;
- /** 8-bit encoding scheme (see TS 23.038) */
public static final int ENCODING_8BIT = 2;
- /** 16-bit encoding scheme (see TS 23.038) */
public static final int ENCODING_16BIT = 3;
/** The maximum number of payload bytes per message */
public static final int MAX_USER_DATA_BYTES = 140;
/**
+ * TODO(cleanup): It would be more flexible and less fragile to
+ * rewrite this (meaning get rid of the following constant) such
+ * that an actual UDH is taken into consideration (meaning its
+ * length is measured), allowing for messages that actually
+ * contain other UDH fields... Hence it is actually a shame to
+ * extend the API with this constant. If necessary, maybe define
+ * the size of such a header and let the math for calculating
+ * max_octets/septets be done elsewhere. And, while I am griping,
+ * if we use the word septet, we should use the word octet in
+ * corresponding places, not byte...
+ */
+
+ /**
* The maximum number of payload bytes per message if a user data header
* is present. This assumes the header only contains the
* CONCATENATED_8_BIT_REFERENCE element.
@@ -222,6 +249,15 @@
}
}
+ /*
+ * TODO(cleanup): It would make some sense if the result of
+ * preprocessing a message to determine the proper encoding (ie
+ * the resulting datastructure from calculateLength) could be
+ * passed as an argument to the actual final encoding function.
+ * This would better ensure that the logic behind size calculation
+ * actually matched the encoding.
+ */
+
/**
* Calculates the number of SMS's required to encode the message body and
* the number of characters remaining until the next message.
@@ -232,46 +268,76 @@
* space chars. If false, and if the messageBody contains
* non-7-bit encodable characters, length is calculated
* using a 16-bit encoding.
- * @return an int[4] with int[0] being the number of SMS's required, int[1]
- * the number of code units used, and int[2] is the number of code
- * units remaining until the next message. int[3] is the encoding
- * type that should be used for the message.
+ * @return an int[4] with int[0] being the number of SMS's
+ * required, int[1] the number of code units used, and
+ * int[2] is the number of code units remaining until the
+ * next message. int[3] is an indicator of the encoding
+ * code unit size (see the ENCODING_* definitions in this
+ * class).
*/
public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
int activePhone = TelephonyManager.getDefault().getPhoneType();
+ TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
+ com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly) :
+ com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly);
int ret[] = new int[4];
+ ret[0] = ted.msgCount;
+ ret[1] = ted.codeUnitCount;
+ ret[2] = ted.codeUnitsRemaining;
+ ret[3] = ted.codeUnitSize;
+ return ret;
+ }
- int septets = (PHONE_TYPE_CDMA == activePhone) ?
- com.android.internal.telephony.cdma.SmsMessage.calc7bitEncodedLength(msgBody,
- use7bitOnly) :
- com.android.internal.telephony.gsm.SmsMessage.calc7bitEncodedLength(msgBody,
- use7bitOnly);
- if (septets != -1) {
- ret[1] = septets;
- if (septets > MAX_USER_DATA_SEPTETS) {
- ret[0] = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
- ret[2] = MAX_USER_DATA_SEPTETS_WITH_HEADER
- - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
- } else {
- ret[0] = 1;
- ret[2] = MAX_USER_DATA_SEPTETS - septets;
- }
- ret[3] = ENCODING_7BIT;
+ /**
+ * Divide a message text into several fragments, none bigger than
+ * the maximum SMS message text size.
+ *
+ * @param text text, must not be null.
+ * @return an <code>ArrayList</code> of strings that, in order,
+ * comprise the original msg text
+ */
+ public static ArrayList<String> fragmentText(String text) {
+ int activePhone = TelephonyManager.getDefault().getPhoneType();
+ TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
+ com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false) :
+ com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false);
+
+ // TODO(cleanup): The code here could be rolled into the logic
+ // below cleanly if these MAX_* constants were defined more
+ // flexibly...
+
+ int limit;
+ if (ted.msgCount > 1) {
+ limit = (ted.codeUnitSize == ENCODING_7BIT) ?
+ MAX_USER_DATA_SEPTETS_WITH_HEADER : MAX_USER_DATA_BYTES_WITH_HEADER;
} else {
- int octets = msgBody.length() * 2;
- ret[1] = msgBody.length();
- if (octets > MAX_USER_DATA_BYTES) {
- ret[0] = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
- ret[2] = (MAX_USER_DATA_BYTES_WITH_HEADER
- - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
- } else {
- ret[0] = 1;
- ret[2] = (MAX_USER_DATA_BYTES - octets)/2;
- }
- ret[3] = ENCODING_16BIT;
+ limit = (ted.codeUnitSize == ENCODING_7BIT) ?
+ MAX_USER_DATA_SEPTETS : MAX_USER_DATA_BYTES;
}
- return ret;
+ int pos = 0; // Index in code units.
+ int textLen = text.length();
+ ArrayList<String> result = new ArrayList<String>(ted.msgCount);
+ while (pos < textLen) {
+ int nextPos = 0; // Counts code units.
+ if (ted.codeUnitSize == ENCODING_7BIT) {
+ if (PHONE_TYPE_CDMA == activePhone) {
+ nextPos = pos + Math.min(limit, textLen - pos);
+ } else {
+ nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit);
+ }
+ } else { // Assume unicode.
+ nextPos = pos + Math.min(limit / 2, textLen - pos);
+ }
+ if ((nextPos <= pos) || (nextPos > textLen)) {
+ Log.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
+ nextPos + " >= " + textLen + ")");
+ break;
+ }
+ result.add(text.substring(pos, nextPos));
+ pos = nextPos;
+ }
+ return result;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index 8e2941b..e8095e1 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -576,52 +576,6 @@
return size;
}
- /**
- * Returns the index into <code>s</code> of the first character
- * after <code>limit</code> octets have been reached, starting at
- * index <code>start</code>. This is used when dividing messages
- * in UCS2 encoding into units within the SMS message size limit.
- *
- * @param s source string
- * @param start index of where to start counting septets
- * @param limit maximum septets to include,
- * e.g. <code>MAX_USER_DATA_BYTES</code>
- * @return index of first character that won't fit, or the length
- * of the entire string if everything fits
- */
- public static int
- findUCS2LimitIndex(String s, int start, int limit) {
- int numCharToBeEncoded = s.length() - start;
- return ((numCharToBeEncoded*2 > limit)? limit/2: numCharToBeEncoded) + start;
- }
-
- /**
- * Returns the index into <code>s</code> of the first character
- * after <code>limit</code> septets/octets have been reached
- * according to the <code>encodingType</code>, starting at
- * index <code>start</code>. This is used when dividing messages
- * units within the SMS message size limit.
- *
- * @param s source string
- * @param start index of where to start counting septets
- * @param limit maximum septets to include,
- * e.g. <code>MAX_USER_DATA_BYTES</code>
- * @return index of first character that won't fit, or the length
- * of the entire string if everything fits
- */
- public static int
- findLimitIndex(String s, int start, int limit, int encodingType) throws EncodeException {
- if (encodingType == SmsMessage.ENCODING_7BIT) {
- return findGsmSeptetLimitIndex(s, start, limit);
- }
- else if (encodingType == SmsMessage.ENCODING_16BIT) {
- return findUCS2LimitIndex(s, start, limit);
- }
- else {
- throw new EncodeException("Unsupported encoding type: " + encodingType);
- }
- }
-
// Set in the static initializer
private static int sGsmSpaceChar;
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index 4d32c35..3c7dd45 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -86,6 +86,38 @@
/** TP-Message-Reference - Message Reference of sent message. @hide */
public int messageRef;
+ /**
+ * For a specific text string, this object describes protocol
+ * properties of encoding it for transmission as message user
+ * data.
+ */
+ public static class TextEncodingDetails {
+ /**
+ *The number of SMS's required to encode the text.
+ */
+ public int msgCount;
+
+ /**
+ * The number of code units consumed so far, where code units
+ * are basically characters in the encoding -- for example,
+ * septets for the standard ASCII and GSM encodings, and 16
+ * bits for Unicode.
+ */
+ public int codeUnitCount;
+
+ /**
+ * How many code units are still available without spilling
+ * into an additional message.
+ */
+ public int codeUnitsRemaining;
+
+ /**
+ * The encoding code unit size (specified using
+ * android.telephony.SmsMessage ENCODING_*).
+ */
+ public int codeUnitSize;
+ }
+
public static abstract class SubmitPduBase {
public byte[] encodedScAddress; // Null if not applicable.
public byte[] encodedMessage;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 79e1cd6..2b4a700 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -34,6 +34,7 @@
import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.cdma.SmsMessage;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
+import com.android.internal.telephony.cdma.sms.UserData;
import com.android.internal.util.HexDump;
import java.io.ByteArrayOutputStream;
@@ -302,8 +303,12 @@
deliveryIntent = deliveryIntents.get(i);
}
- SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(scAddr, destAddr,
- parts.get(i), deliveryIntent != null, smsHeader);
+ UserData uData = new UserData();
+ uData.payloadStr = parts.get(i);
+ uData.userDataHeader = smsHeader;
+
+ SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destAddr,
+ uData, deliveryIntent != null);
sendSubmitPdu(submitPdu, sentIntent, deliveryIntent);
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index bbdd0dd..63d2c47 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -357,14 +357,18 @@
}
/**
- * Calculate the number of septets needed to encode the message.
+ * Get an SMS-SUBMIT PDU for a data message to a destination address & port
*
- * @param messageBody the message to encode
- * @param force ignore (but still count) illegal characters if true
- * @return septet count, or -1 on failure
+ * @param destAddr the address of the destination for the message
+ * @param userDara the data for the message
+ * @param statusReportRequested Indicates whether a report is requested for this message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC
+ * address, if applicable, and the encoded message.
+ * Returns null on encode error.
*/
- public static int calc7bitEncodedLength(CharSequence msgBody, boolean force) {
- return BearerData.calc7bitEncodedLength(msgBody.toString(), force);
+ public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
+ boolean statusReportRequested) {
+ return privateGetSubmitPdu(destAddr, statusReportRequested, userData);
}
/**
@@ -442,6 +446,18 @@
}
/**
+ * Calculate the number of septets needed to encode the message.
+ *
+ * @param messageBody the message to encode
+ * @param use7bitOnly ignore (but still count) illegal characters if true
+ * @return TextEncodingDetails
+ */
+ public static TextEncodingDetails calculateLength(CharSequence messageBody,
+ boolean use7bitOnly) {
+ return BearerData.calcTextEncodingDetails(messageBody.toString(), use7bitOnly);
+ }
+
+ /**
* Returns the teleservice type of the message.
* @return the teleservice:
* {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_NOT_SET},
@@ -627,12 +643,15 @@
bearerData.userData = userData;
bearerData.hasUserDataHeader = (userData.userDataHeader != null);
+ int teleservice = bearerData.hasUserDataHeader ?
+ SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
+
byte[] encodedBearerData = BearerData.encode(bearerData);
if (encodedBearerData == null) return null;
SmsEnvelope envelope = new SmsEnvelope();
envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
- envelope.teleService = SmsEnvelope.TELESERVICE_WMT;
+ envelope.teleService = teleservice;
envelope.destAddress = destAddr;
envelope.bearerReply = RETURN_ACK;
envelope.bearerData = encodedBearerData;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 3c45aa4..a835dee 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cdma.sms;
import android.util.Log;
+import android.util.SparseIntArray;
import android.telephony.SmsMessage;
@@ -26,6 +27,7 @@
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
import com.android.internal.util.HexDump;
import com.android.internal.util.BitwiseInputStream;
@@ -35,7 +37,7 @@
/**
* An object to encode and decode CDMA SMS bearer data.
*/
-public final class BearerData{
+public final class BearerData {
private final static String LOG_TAG = "SMS";
/**
@@ -385,56 +387,61 @@
outStream.skip(3);
}
- private static class SeptetData {
- byte data[];
- int septetCount;
-
- SeptetData(byte[] data, int septetCount) {
- this.data = data;
- this.septetCount = septetCount;
- }
- }
-
- private static SeptetData encode7bitAscii(String msg, boolean force)
- throws CodingException
- {
- try {
- BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length());
- byte[] expandedData = msg.getBytes("US-ASCII");
- for (int i = 0; i < expandedData.length; i++) {
- int charCode = expandedData[i];
- // Test ourselves for ASCII membership, since Java seems not to care.
- if ((charCode < UserData.PRINTABLE_ASCII_MIN_INDEX) ||
- (charCode > UserData.PRINTABLE_ASCII_MAX_INDEX)) {
- if (force) {
- outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
- } else {
- throw new CodingException("illegal ASCII code (" + charCode + ")");
- }
- } else {
- outStream.write(7, expandedData[i]);
- }
+ private static int countAsciiSeptets(CharSequence msg, boolean force) {
+ int msgLen = msg.length();
+ if (force) return msgLen;
+ for (int i = 0; i < msgLen; i++) {
+ if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) {
+ return -1;
}
- return new SeptetData(outStream.toByteArray(), expandedData.length);
- } catch (java.io.UnsupportedEncodingException ex) {
- throw new CodingException("7bit ASCII encode failed: " + ex);
- } catch (BitwiseOutputStream.AccessException ex) {
- throw new CodingException("7bit ASCII encode failed: " + ex);
}
+ return msgLen;
}
/**
- * Calculate the number of septets needed to encode the message.
+ * Calculate the message text encoding length, fragmentation, and other details.
*
* @param force ignore (but still count) illegal characters if true
* @return septet count, or -1 on failure
*/
- public static int calc7bitEncodedLength(String msg, boolean force) {
+ public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg,
+ boolean force7BitEncoding) {
+ TextEncodingDetails ted;
+ int septets = countAsciiSeptets(msg, force7BitEncoding);
+ if (septets != -1 && septets <= SmsMessage.MAX_USER_DATA_SEPTETS) {
+ ted = new TextEncodingDetails();
+ ted.msgCount = 1;
+ ted.codeUnitCount = septets;
+ ted.codeUnitsRemaining = SmsMessage.MAX_USER_DATA_SEPTETS - septets;
+ ted.codeUnitSize = SmsMessage.ENCODING_7BIT;
+ } else {
+ ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength(
+ msg, force7BitEncoding);
+ }
+ return ted;
+ }
+
+ private static byte[] encode7bitAscii(String msg, boolean force)
+ throws CodingException
+ {
try {
- SeptetData data = encode7bitAscii(msg, force);
- return data.septetCount;
- } catch (CodingException ex) {
- return -1;
+ BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length());
+ int msgLen = msg.length();
+ for (int i = 0; i < msgLen; i++) {
+ int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
+ if (charCode == -1) {
+ if (force) {
+ outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
+ } else {
+ throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
+ }
+ } else {
+ outStream.write(7, charCode);
+ }
+ }
+ return outStream.toByteArray();
+ } catch (BitwiseOutputStream.AccessException ex) {
+ throw new CodingException("7bit ASCII encode failed: " + ex);
}
}
@@ -452,8 +459,10 @@
throws CodingException
{
try {
- /**
- * TODO(cleanup): find some way to do this without the copy.
+ /*
+ * TODO(cleanup): It would be nice if GsmAlphabet provided
+ * an option to produce just the data without prepending
+ * the length.
*/
byte []fullData = GsmAlphabet.stringToGsm7BitPacked(msg);
byte []data = new byte[fullData.length - 1];
@@ -470,54 +479,65 @@
throws CodingException
{
byte[] headerData = null;
+ // TODO: if there is a header, meaning EMS mode, we probably
+ // also want the total UD length prior to the UDH length...
if (uData.userDataHeader != null) headerData = SmsHeader.toByteArray(uData.userDataHeader);
int headerDataLen = (headerData == null) ? 0 : headerData.length + 1; // + length octet
byte[] payloadData;
+ int codeUnitCount;
if (uData.msgEncodingSet) {
if (uData.msgEncoding == UserData.ENCODING_OCTET) {
if (uData.payload == null) {
Log.e(LOG_TAG, "user data with octet encoding but null payload");
- // TODO(code_review): reasonable for fail case? or maybe bail on encoding?
payloadData = new byte[0];
+ codeUnitCount = 0;
} else {
payloadData = uData.payload;
+ codeUnitCount = uData.payload.length;
}
} else {
if (uData.payloadStr == null) {
Log.e(LOG_TAG, "non-octet user data with null payloadStr");
- // TODO(code_review): reasonable for fail case? or maybe bail on encoding?
uData.payloadStr = "";
}
if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
payloadData = encode7bitGsm(uData.payloadStr);
+ codeUnitCount = (payloadData.length * 8) / 7;
} else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
- SeptetData septetData = encode7bitAscii(uData.payloadStr, true);
- payloadData = septetData.data;
+ payloadData = encode7bitAscii(uData.payloadStr, true);
+ codeUnitCount = uData.payloadStr.length();
} else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
payloadData = encodeUtf16(uData.payloadStr);
+ codeUnitCount = uData.payloadStr.length();
} else {
throw new CodingException("unsupported user data encoding (" +
uData.msgEncoding + ")");
}
- uData.numFields = uData.payloadStr.length();
}
} else {
if (uData.payloadStr == null) {
Log.e(LOG_TAG, "user data with null payloadStr");
- // TODO(code_review): reasonable for fail case? or maybe bail on encoding?
uData.payloadStr = "";
}
try {
- SeptetData septetData = encode7bitAscii(uData.payloadStr, false);
- payloadData = septetData.data;
- uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
+ if (headerData == null) {
+ payloadData = encode7bitAscii(uData.payloadStr, false);
+ codeUnitCount = uData.payloadStr.length();
+ uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
+ } else {
+ // If there is a header, we are in EMS mode, in
+ // which case we use GSM encodings.
+ payloadData = encode7bitGsm(uData.payloadStr);
+ codeUnitCount = (payloadData.length * 8) / 7;
+ uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
+ }
} catch (CodingException ex) {
payloadData = encodeUtf16(uData.payloadStr);
+ codeUnitCount = uData.payloadStr.length();
uData.msgEncoding = UserData.ENCODING_UNICODE_16;
}
uData.msgEncodingSet = true;
- uData.numFields = uData.payloadStr.length();
}
int totalLength = payloadData.length + headerDataLen;
@@ -526,6 +546,7 @@
" > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)");
}
+ uData.numFields = codeUnitCount;
uData.payload = new byte[totalLength];
if (headerData != null) {
uData.payload[0] = (byte)headerData.length;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index 8d4e769..d8a48cc 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.cdma.sms;
+import android.util.SparseIntArray;
+
import com.android.internal.telephony.SmsHeader;
import com.android.internal.util.HexDump;
@@ -40,6 +42,10 @@
/**
* IA5 data encoding character mappings.
* (See CCITT Rec. T.50 Tables 1 and 3)
+ *
+ * Note this mapping is the the same as for printable ASCII
+ * characters, with a 0x20 offset, meaning that the ASCII SPACE
+ * character occurs with code 0x20.
*/
public static final char[] IA5_MAP = {
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
@@ -61,7 +67,16 @@
* Only elements between these indices in the ASCII table are printable.
*/
public static final int PRINTABLE_ASCII_MIN_INDEX = 0x20;
- public static final int PRINTABLE_ASCII_MAX_INDEX = 0x7F;
+ public static final int ASCII_LF_INDEX = 0x0A;
+ public static final int ASCII_CR_INDEX = 0x0D;
+ public static final SparseIntArray charToAscii = new SparseIntArray();
+ static {
+ for (int i = 0; i < IA5_MAP.length; i++) {
+ charToAscii.put(IA5_MAP[i], PRINTABLE_ASCII_MIN_INDEX + i);
+ }
+ charToAscii.put('\r', ASCII_LF_INDEX);
+ charToAscii.put('\n', ASCII_CR_INDEX);
+ }
/**
* Mapping for IA5 values less than 32 are flow control signals
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index a15bbdf..f1207e4 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -26,6 +26,7 @@
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
@@ -742,17 +743,39 @@
/**
* Calculate the number of septets needed to encode the message.
*
- * @param messageBody the message to encode
- * @param force ignore (but still count) illegal characters if true
- * @return septet count, or -1 on failure
+ * @param msgBody the message to encode
+ * @param use7bitOnly ignore (but still count) illegal characters if true
+ * @return TextEncodingDetails
*/
- public static int calc7bitEncodedLength(CharSequence messageBody, boolean force) {
+ public static TextEncodingDetails calculateLength(CharSequence msgBody,
+ boolean use7bitOnly) {
+ TextEncodingDetails ted = new TextEncodingDetails();
try {
- return GsmAlphabet.countGsmSeptets(messageBody, !force);
+ int septets = GsmAlphabet.countGsmSeptets(msgBody, !use7bitOnly);
+ ted.codeUnitCount = septets;
+ if (septets > MAX_USER_DATA_SEPTETS) {
+ ted.msgCount = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1;
+ ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS_WITH_HEADER
+ - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER);
+ } else {
+ ted.msgCount = 1;
+ ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets;
+ }
+ ted.codeUnitSize = ENCODING_7BIT;
} catch (EncodeException ex) {
- /* Just fall through to the -1 error result below. */
+ int octets = msgBody.length() * 2;
+ ted.codeUnitCount = msgBody.length();
+ if (octets > MAX_USER_DATA_BYTES) {
+ ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1;
+ ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER
+ - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2;
+ } else {
+ ted.msgCount = 1;
+ ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
+ }
+ ted.codeUnitSize = ENCODING_16BIT;
}
- return -1;
+ return ted;
}
/** {@inheritDoc} */
diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java
index 9e0cf2c..efc4880 100644
--- a/test-runner/android/test/mock/MockContext.java
+++ b/test-runner/android/test/mock/MockContext.java
@@ -24,6 +24,7 @@
import android.content.BroadcastReceiver;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
@@ -100,6 +101,11 @@
}
@Override
+ public ApplicationInfo getApplicationInfo() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public String getPackageResourcePath() {
throw new UnsupportedOperationException();
}
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index 5e7802d..399a053 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -63,11 +63,6 @@
}
@Override
- public ResolveInfo resolveActivity(Intent intent, int flags, String packageName) {
- throw new UnsupportedOperationException();
- }
-
- @Override
public int[] getPackageGids(String packageName) throws NameNotFoundException {
throw new UnsupportedOperationException();
}
diff --git a/tests/backup/src/com/android/backuptest/BackupTestActivity.java b/tests/backup/src/com/android/backuptest/BackupTestActivity.java
index af7dfd4..f0c3f93 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestActivity.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestActivity.java
@@ -17,14 +17,17 @@
package com.android.backuptest;
import android.app.ListActivity;
+import android.backup.BackupDataInput;
+import android.backup.BackupDataOutput;
import android.backup.BackupManager;
+import android.backup.FileBackupHelper;
+import android.backup.FileRestoreHelper;
+import android.backup.RestoreHelperDispatcher;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.os.PowerManager;
-import android.os.SystemClock;
+import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
@@ -32,6 +35,10 @@
import android.widget.Toast;
import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintStream;
@@ -123,6 +130,48 @@
BackupManager bm = new BackupManager(BackupTestActivity.this);
bm.dataChanged();
}
+ },
+ new Test("Backup Helpers") {
+ void run() {
+ try {
+ writeFile("a", "a\naa", MODE_PRIVATE);
+ writeFile("empty", "", MODE_PRIVATE);
+
+ ParcelFileDescriptor state = ParcelFileDescriptor.open(
+ new File(getFilesDir(), "state"),
+ ParcelFileDescriptor.MODE_READ_WRITE|ParcelFileDescriptor.MODE_CREATE|
+ ParcelFileDescriptor.MODE_TRUNCATE);
+ FileBackupHelper h = new FileBackupHelper(BackupTestActivity.this,
+ "FileBackupHelper");
+ FileOutputStream dataFile = openFileOutput("backup_test", MODE_WORLD_READABLE);
+ BackupDataOutput data = new BackupDataOutput(dataFile.getFD());
+ h.performBackup(null, data, state, new String[] { "a", "empty" });
+ dataFile.close();
+ state.close();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ },
+ new Test("Restore Helpers") {
+ void run() {
+ try {
+ RestoreHelperDispatcher dispatch = new RestoreHelperDispatcher();
+ dispatch.addHelper("FileBackupHelper",
+ new FileRestoreHelper(BackupTestActivity.this));
+ FileInputStream dataFile = openFileInput("backup_test");
+ BackupDataInput data = new BackupDataInput(dataFile.getFD());
+ ParcelFileDescriptor state = ParcelFileDescriptor.open(
+ new File(getFilesDir(), "restore_state"),
+ ParcelFileDescriptor.MODE_READ_WRITE|ParcelFileDescriptor.MODE_CREATE|
+ ParcelFileDescriptor.MODE_TRUNCATE);
+ dispatch.dispatch(data, state);
+ dataFile.close();
+ state.close();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
}
};
@@ -154,5 +203,14 @@
t.run();
}
+ void writeFile(String name, String contents, int mode) {
+ try {
+ PrintStream out = new PrintStream(openFileOutput(name, mode));
+ out.print(contents);
+ out.close();
+ } catch (FileNotFoundException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
}
diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
index a370d69..0da4151 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
@@ -17,28 +17,45 @@
package com.android.backuptest;
import android.app.BackupAgent;
+import android.backup.BackupDataInput;
import android.backup.BackupDataOutput;
import android.backup.FileBackupHelper;
+import android.backup.FileRestoreHelper;
+import android.backup.RestoreHelperDispatcher;
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import java.io.IOException;
+
public class BackupTestAgent extends BackupAgent
{
static final String TAG = "BackupTestAgent";
+ static final String SHARED_PREFS = "shared_prefs";
+ static final String DATA_FILES = "data_files";
+ static final String[] FILES = new String[] {
+ BackupTestActivity.FILE_NAME
+ };
+
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) {
Log.d(TAG, "onBackup");
- FileBackupHelper helper = new FileBackupHelper(this);
- helper.performBackup(oldState, data, newState, new String[] {
- BackupTestActivity.FILE_NAME
- });
+
+ (new FileBackupHelper(this, DATA_FILES)).performBackup(oldState, data, newState, FILES);
}
@Override
- public void onRestore(ParcelFileDescriptor data, ParcelFileDescriptor newState) {
+ public void onRestore(BackupDataInput data, ParcelFileDescriptor newState)
+ throws IOException {
Log.d(TAG, "onRestore");
+
+ RestoreHelperDispatcher dispatch = new RestoreHelperDispatcher();
+
+ // dispatch.addHelper(SHARED_PREFS, new SharedPrefsRestoreHelper(this));
+ dispatch.addHelper(DATA_FILES, new FileRestoreHelper(this));
+
+ dispatch.dispatch(data, newState);
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 47a7ec0..d0a1c46 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -55,6 +55,8 @@
import android.view.View.MeasureSpec;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
+import android.widget.TabHost;
+import android.widget.TabWidget;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
@@ -69,10 +71,10 @@
* {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}.
*/
public final class Bridge implements ILayoutBridge {
-
+
private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
-
+
public static class StaticMethodNotImplementedException extends RuntimeException {
private static final long serialVersionUID = 1L;
@@ -82,19 +84,20 @@
}
/**
- * Maps from id to resource name/type.
+ * Maps from id to resource name/type. This is for android.R only.
*/
private final static Map<Integer, String[]> sRMap = new HashMap<Integer, String[]>();
/**
- * Same as sRMap except for int[] instead of int resources.
+ * Same as sRMap except for int[] instead of int resources. This is for android.R only.
*/
private final static Map<int[], String> sRArrayMap = new HashMap<int[], String>();
/**
- * Reverse map compared to sRMap, resource type -> (resource name -> id)
+ * Reverse map compared to sRMap, resource type -> (resource name -> id).
+ * This is for android.R only.
*/
private final static Map<String, Map<String, Integer>> sRFullMap =
new HashMap<String, Map<String,Integer>>();
-
+
private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache =
new HashMap<Object, Map<String, SoftReference<Bitmap>>>();
private final static Map<Object, Map<String, SoftReference<NinePatch>>> sProject9PatchCache =
@@ -104,7 +107,7 @@
new HashMap<String, SoftReference<Bitmap>>();
private final static Map<String, SoftReference<NinePatch>> sFramework9PatchCache =
new HashMap<String, SoftReference<NinePatch>>();
-
+
private static Map<String, Map<String, Integer>> sEnumValueMap;
/**
@@ -156,14 +159,14 @@
return sinit(fontOsLocation, enumValueMap);
}
-
+
private static synchronized boolean sinit(String fontOsLocation,
Map<String, Map<String, Integer>> enumValueMap) {
// When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener
// on static (native) methods which prints the signature on the console and
// throws an exception.
- // This is useful when testing the rendering in ADT to identify static native
+ // This is useful when testing the rendering in ADT to identify static native
// methods that are ignored -- layoutlib_create makes them returns 0/false/null
// which is generally OK yet might be a problem, so this is how you'd find out.
//
@@ -214,7 +217,7 @@
} else {
return false;
}
-
+
sEnumValueMap = enumValueMap;
// now parse com.android.internal.R (and only this one as android.R is a subset of
@@ -226,13 +229,13 @@
// int[] does not implement equals/hashCode, and if the parsing used a different class
// loader for the R class, this would NOT work.
Class<?> r = com.android.internal.R.class;
-
+
for (Class<?> inner : r.getDeclaredClasses()) {
String resType = inner.getSimpleName();
Map<String, Integer> fullMap = new HashMap<String, Integer>();
sRFullMap.put(resType, fullMap);
-
+
for (Field f : inner.getDeclaredFields()) {
// only process static final fields. Since the final attribute may have
// been altered by layoutlib_create, we only check static
@@ -243,7 +246,7 @@
// if the object is an int[] we put it in sRArrayMap
sRArrayMap.put((int[]) f.get(null), f.getName());
} else if (type == int.class) {
- Integer value = (Integer) f.get(null);
+ Integer value = (Integer) f.get(null);
sRMap.put(value, new String[] { f.getName(), resType });
fullMap.put(f.getName(), value);
} else {
@@ -281,7 +284,7 @@
themeName = themeName.substring(1);
isProjectTheme = true;
}
-
+
return computeLayout(layoutDescription, projectKey,
screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY,
DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY,
@@ -294,6 +297,7 @@
* (non-Javadoc)
* @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
*/
+ @Deprecated
public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey,
int screenWidth, int screenHeight, String themeName, boolean isProjectTheme,
Map<String, Map<String, IResourceValue>> projectResources,
@@ -319,7 +323,7 @@
if (logger == null) {
logger = sDefaultLogger;
}
-
+
synchronized (sDefaultLogger) {
sLogger = logger;
}
@@ -327,12 +331,12 @@
// find the current theme and compute the style inheritance map
Map<IStyleResourceValue, IStyleResourceValue> styleParentMap =
new HashMap<IStyleResourceValue, IStyleResourceValue>();
-
+
IStyleResourceValue currentTheme = computeStyleMaps(themeName, isProjectTheme,
projectResources.get(BridgeConstants.RES_STYLE),
frameworkResources.get(BridgeConstants.RES_STYLE), styleParentMap);
-
- BridgeContext context = null;
+
+ BridgeContext context = null;
try {
// setup the display Metrics.
DisplayMetrics metrics = new DisplayMetrics();
@@ -347,29 +351,32 @@
frameworkResources, styleParentMap, customViewLoader, logger);
BridgeInflater inflater = new BridgeInflater(context, customViewLoader);
context.setBridgeInflater(inflater);
-
+
IResourceValue windowBackground = null;
int screenOffset = 0;
if (currentTheme != null) {
windowBackground = context.findItemInStyle(currentTheme, "windowBackground");
windowBackground = context.resolveResValue(windowBackground);
-
+
screenOffset = getScreenOffset(currentTheme, context);
}
-
+
// we need to make sure the Looper has been initialized for this thread.
// this is required for View that creates Handler objects.
if (Looper.myLooper() == null) {
Looper.prepare();
}
-
+
BridgeXmlBlockParser parser = new BridgeXmlBlockParser(layoutDescription,
context, false /* platformResourceFlag */);
-
+
ViewGroup root = new FrameLayout(context);
-
+
View view = inflater.inflate(parser, root);
-
+
+ // post-inflate process. For now this supports TabHost/TabWidget
+ postInflateProcess(view, customViewLoader);
+
// set the AttachInfo on the root view.
AttachInfo info = new AttachInfo(new WindowSession(), new Window(),
new Handler(), null);
@@ -392,16 +399,19 @@
// measure the views
view.measure(w_spec, h_spec);
view.layout(0, screenOffset, screenWidth, screenHeight);
-
+
// draw them
BridgeCanvas canvas = new BridgeCanvas(screenWidth, screenHeight - screenOffset,
logger);
-
+
root.draw(canvas);
canvas.dispose();
-
+
return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context),
canvas.getImage());
+ } catch (PostInflateException e) {
+ return new LayoutResult(ILayoutResult.ERROR, "Error during post inflation process:\n"
+ + e.getMessage());
} catch (Throwable e) {
// get the real cause of the exception.
Throwable t = e;
@@ -419,7 +429,7 @@
// Make sure to remove static references, otherwise we could not unload the lib
BridgeResources.clearSystem();
BridgeAssetManager.clearSystem();
-
+
// Remove the global logger
synchronized (sDefaultLogger) {
sLogger = sDefaultLogger;
@@ -437,18 +447,18 @@
sProject9PatchCache.remove(projectKey);
}
}
-
+
/**
* Returns details of a framework resource from its integer value.
* @param value the integer value
* @return an array of 2 strings containing the resource name and type, or null if the id
- * does not match any resource.
+ * does not match any resource.
*/
public static String[] resolveResourceValue(int value) {
return sRMap.get(value);
-
+
}
-
+
/**
* Returns the name of a framework resource whose value is an int array.
* @param array
@@ -456,7 +466,7 @@
public static String resolveResourceValue(int[] array) {
return sRArrayMap.get(array);
}
-
+
/**
* Returns the integer id of a framework resource, from a given resource type and resource name.
* @param type the type of the resource
@@ -468,15 +478,15 @@
if (map != null) {
return map.get(name);
}
-
+
return null;
}
-
+
static Map<String, Integer> getEnumValues(String attributeName) {
if (sEnumValueMap != null) {
return sEnumValueMap.get(attributeName);
}
-
+
return null;
}
@@ -507,13 +517,13 @@
return result;
}
-
+
/**
* Compute style information from the given list of style for the project and framework.
* @param themeName the name of the current theme. In order to differentiate project and
* platform themes sharing the same name, all project themes must be prepended with
* a '*' character.
- * @param isProjectTheme Is this a project theme
+ * @param isProjectTheme Is this a project theme
* @param inProjectStyleMap the project style map
* @param inFrameworkStyleMap the framework style map
* @param outInheritanceMap the map of style inheritance. This is filled by the method
@@ -523,23 +533,23 @@
String themeName, boolean isProjectTheme, Map<String,
IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap,
Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) {
-
+
if (inProjectStyleMap != null && inFrameworkStyleMap != null) {
// first, get the theme
IResourceValue theme = null;
-
+
// project theme names have been prepended with a *
if (isProjectTheme) {
theme = inProjectStyleMap.get(themeName);
} else {
theme = inFrameworkStyleMap.get(themeName);
}
-
+
if (theme instanceof IStyleResourceValue) {
// compute the inheritance map for both the project and framework styles
computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap,
inFrameworkStyleMap, outInheritanceMap);
-
+
// Compute the style inheritance for the framework styles/themes.
// Since, for those, the style parent values do not contain 'android:'
// we want to force looking in the framework style only to avoid using
@@ -547,11 +557,11 @@
// To do this, we pass null in lieu of the project style map.
computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */,
inFrameworkStyleMap, outInheritanceMap);
-
+
return (IStyleResourceValue)theme;
}
}
-
+
return null;
}
@@ -573,7 +583,7 @@
// first look for a specified parent.
String parentName = style.getParentStyle();
-
+
// no specified parent? try to infer it from the name of the style.
if (parentName == null) {
parentName = getParentName(value.getName());
@@ -581,7 +591,7 @@
if (parentName != null) {
parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap);
-
+
if (parentStyle != null) {
outInheritanceMap.put(style, parentStyle);
}
@@ -589,7 +599,7 @@
}
}
}
-
+
/**
* Searches for and returns the {@link IStyleResourceValue} from a given name.
* <p/>The format of the name can be:
@@ -607,27 +617,27 @@
Map<String, IResourceValue> inProjectStyleMap,
Map<String, IResourceValue> inFrameworkStyleMap) {
boolean frameworkOnly = false;
-
+
String name = parentName;
-
+
// remove the useless @ if it's there
if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) {
name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length());
}
-
+
// check for framework identifier.
if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) {
frameworkOnly = true;
name = name.substring(BridgeConstants.PREFIX_ANDROID.length());
}
-
+
// at this point we could have the format style/<name>. we want only the name
if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) {
name = name.substring(BridgeConstants.REFERENCE_STYLE.length());
}
IResourceValue parent = null;
-
+
// if allowed, search in the project resources.
if (frameworkOnly == false && inProjectStyleMap != null) {
parent = inProjectStyleMap.get(name);
@@ -637,17 +647,17 @@
if (parent == null) {
parent = inFrameworkStyleMap.get(name);
}
-
+
// make sure the result is the proper class type and return it.
if (parent instanceof IStyleResourceValue) {
return (IStyleResourceValue)parent;
}
-
+
sLogger.error(String.format("Unable to resolve parent style name: ", parentName));
-
+
return null;
}
-
+
/**
* Computes the name of the parent style, or <code>null</code> if the style is a root style.
*/
@@ -656,10 +666,10 @@
if (index != -1) {
return styleName.substring(0, index);
}
-
+
return null;
}
-
+
/**
* Returns the top screen offset. This depends on whether the current theme defines the user
* of the title and status bars.
@@ -670,7 +680,7 @@
// get the title bar flag from the current theme.
IResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle");
-
+
// because it may reference something else, we resolve it.
value = context.resolveResValue(value);
@@ -679,10 +689,10 @@
XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
// get value from the theme.
value = context.findItemInStyle(currentTheme, "windowTitleSize");
-
+
// resolve it
value = context.resolveResValue(value);
-
+
// default value
offset = DEFAULT_TITLE_BAR_HEIGHT;
@@ -690,17 +700,17 @@
if (value != null) {
TypedValue typedValue = ResourceHelper.getValue(value.getValue());
if (typedValue != null) {
- offset = (int)typedValue.getDimension(context.getResources().mMetrics);
+ offset = (int)typedValue.getDimension(context.getResources().mMetrics);
}
}
}
-
+
// get the fullscreen flag from the current theme.
value = context.findItemInStyle(currentTheme, "windowFullscreen");
-
+
// because it may reference something else, we resolve it.
value = context.resolveResValue(value);
-
+
if (value == null || value.getValue() == null ||
XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
// FIXME: Right now this is hard-coded in the platform, but once there's a constant, we'll need to use it.
@@ -711,6 +721,94 @@
}
/**
+ * Post process on a view hierachy that was just inflated.
+ * <p/>At the moment this only support TabHost: If {@link TabHost} is detected, look for the
+ * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically
+ * based on the content of the {@link FrameLayout}.
+ * @param view the root view to process.
+ * @param projectCallback callback to the project.
+ */
+ private void postInflateProcess(View view, IProjectCallback projectCallback)
+ throws PostInflateException {
+ if (view instanceof TabHost) {
+ setupTabHost((TabHost)view, projectCallback);
+ } else if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup)view;
+ final int count = group.getChildCount();
+ for (int c = 0 ; c < count ; c++) {
+ View child = group.getChildAt(c);
+ postInflateProcess(child, projectCallback);
+ }
+ }
+ }
+
+ /**
+ * Sets up a {@link TabHost} object.
+ * @param tabHost the TabHost to setup.
+ * @param projectCallback The project callback object to access the project R class.
+ * @throws PostInflateException
+ */
+ private void setupTabHost(TabHost tabHost, IProjectCallback projectCallback)
+ throws PostInflateException {
+ // look for the TabWidget, and the FrameLayout. They have their own specific names
+ View v = tabHost.findViewById(android.R.id.tabs);
+
+ if (v == null) {
+ throw new PostInflateException(
+ "TabHost requires a TabWidget with id \"android:id/tabs\".\n");
+ }
+
+ if ((v instanceof TabWidget) == false) {
+ throw new PostInflateException(String.format(
+ "TabHost requires a TabWidget with id \"android:id/tabs\".\n" +
+ "View found with id 'tabs' is '%s'", v.getClass().getCanonicalName()));
+ }
+
+ v = tabHost.findViewById(android.R.id.tabcontent);
+
+ if (v == null) {
+ // TODO: see if we can fake tabs even without the FrameLayout (same below when the framelayout is empty)
+ throw new PostInflateException(
+ "TabHost requires a FrameLayout with id \"android:id/tabcontent\".");
+ }
+
+ if ((v instanceof FrameLayout) == false) {
+ throw new PostInflateException(String.format(
+ "TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" +
+ "View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName()));
+ }
+
+ FrameLayout content = (FrameLayout)v;
+
+ // now process the content of the framelayout and dynamically create tabs for it.
+ final int count = content.getChildCount();
+
+ if (count == 0) {
+ throw new PostInflateException(
+ "The FrameLayout for the TabHost has no content. Rendering failed.\n");
+ }
+
+ // this must be called before addTab() so that the TabHost searches its TabWidget
+ // and FrameLayout.
+ tabHost.setup();
+
+ // for each child of the framelayout, add a new TabSpec
+ for (int i = 0 ; i < count ; i++) {
+ View child = content.getChildAt(i);
+ String tabSpec = String.format("tab_spec%d", i+1);
+ int id = child.getId();
+ String[] resource = projectCallback.resolveResourceValue(id);
+ String name;
+ if (resource != null) {
+ name = resource[0]; // 0 is resource name, 1 is resource type.
+ } else {
+ name = String.format("Tab %d", i+1); // default name if id is unresolved.
+ }
+ tabHost.addTab(tabHost.newTabSpec(tabSpec).setIndicator(name).setContent(id));
+ }
+ }
+
+ /**
* Returns the bitmap for a specific path, from a specific project cache, or from the
* framework cache.
* @param value the path of the bitmap
@@ -750,7 +848,7 @@
map = new HashMap<String, SoftReference<Bitmap>>();
sProjectBitmapCache.put(projectKey, map);
}
-
+
map.put(value, new SoftReference<Bitmap>(bmp));
} else {
sFrameworkBitmapCache.put(value, new SoftReference<Bitmap>(bmp));
@@ -767,7 +865,7 @@
static NinePatch getCached9Patch(String value, Object projectKey) {
if (projectKey != null) {
Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey);
-
+
if (map != null) {
SoftReference<NinePatch> ref = map.get(value);
if (ref != null) {
@@ -780,7 +878,7 @@
return ref.get();
}
}
-
+
return null;
}
@@ -798,13 +896,21 @@
map = new HashMap<String, SoftReference<NinePatch>>();
sProject9PatchCache.put(projectKey, map);
}
-
+
map.put(value, new SoftReference<NinePatch>(ninePatch));
} else {
sFramework9PatchCache.put(value, new SoftReference<NinePatch>(ninePatch));
}
}
+ private static final class PostInflateException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public PostInflateException(String message) {
+ super(message);
+ }
+ }
+
/**
* Implementation of {@link IWindowSession} so that mSession is not null in
* the {@link SurfaceView}.
@@ -839,7 +945,7 @@
// pass for now.
return false;
}
-
+
@SuppressWarnings("unused")
public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException {
// pass for now.
@@ -863,7 +969,7 @@
public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
// pass for now.
}
-
+
@SuppressWarnings("unused")
public void remove(IWindow arg0) throws RemoteException {
// pass for now.
@@ -883,13 +989,13 @@
Rect visibleInsets) {
// pass for now.
}
-
+
public IBinder asBinder() {
// pass for now.
return null;
}
}
-
+
/**
* Implementation of {@link IWindow} to pass to the {@link AttachInfo}.
*/
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index d0896b5..69f3d9c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -29,6 +29,7 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Configuration;
@@ -960,6 +961,12 @@
}
@Override
+ public ApplicationInfo getApplicationInfo() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
public String getPackageResourcePath() {
// TODO Auto-generated method stub
return null;