summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.xml112
-rw-r--r--core/java/android/app/Activity.java22
-rw-r--r--core/java/android/app/ApplicationContext.java19
-rw-r--r--core/java/android/app/ApplicationErrorReport.java8
-rw-r--r--core/java/android/app/BackupAgent.java26
-rw-r--r--core/java/android/app/FullBackupAgent.java3
-rw-r--r--core/java/android/backup/BackupDataInputStream.java2
-rw-r--r--core/java/android/backup/BackupDataOutput.java4
-rw-r--r--core/java/android/backup/FileRestoreHelper.java41
-rw-r--r--core/java/android/backup/RestoreHelper.java2
-rw-r--r--core/java/android/backup/RestoreHelperBase.java85
-rw-r--r--core/java/android/backup/RestoreHelperDispatcher.java21
-rw-r--r--core/java/android/content/Intent.java259
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl3
-rw-r--r--core/java/android/content/pm/PackageManager.java17
-rw-r--r--core/java/android/widget/AutoCompleteTextView.java77
-rw-r--r--core/java/android/widget/ListView.java2
-rw-r--r--core/java/com/android/internal/backup/GoogleTransport.java50
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java11
-rw-r--r--core/jni/android_bluetooth_Database.cpp1
-rw-r--r--core/jni/android_server_BluetoothA2dpService.cpp1
-rw-r--r--core/jni/android_server_BluetoothDeviceService.cpp1
-rw-r--r--core/jni/android_server_BluetoothEventLoop.cpp1
-rw-r--r--include/utils/BackupHelpers.h2
-rw-r--r--libs/utils/BackupData.cpp8
-rw-r--r--packages/SettingsProvider/AndroidManifest.xml2
-rw-r--r--packages/SettingsProvider/res/values/strings.xml22
-rw-r--r--packages/SubscribedFeedsProvider/AndroidManifest.xml2
-rw-r--r--packages/SubscribedFeedsProvider/res/values/strings.xml21
-rw-r--r--packages/VpnServices/AndroidManifest.xml2
-rwxr-xr-xpackages/VpnServices/res/values/strings.xml3
-rw-r--r--services/java/com/android/server/BackupManagerService.java77
-rw-r--r--services/java/com/android/server/PackageManagerService.java93
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java14
-rwxr-xr-xservices/java/com/android/server/am/UsageStatsService.java127
-rw-r--r--test-runner/android/test/mock/MockPackageManager.java5
-rw-r--r--tests/backup/src/com/android/backuptest/BackupTestActivity.java59
-rw-r--r--tests/backup/src/com/android/backuptest/BackupTestAgent.java27
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java85
39 files changed, 1014 insertions, 303 deletions
diff --git a/api/current.xml b/api/current.xml
index 2128e5b4acaa..4efc71fe5bef 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -30186,7 +30186,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="uri" type="java.lang.String">
@@ -30237,6 +30237,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 +30447,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 +31137,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 +31170,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"
@@ -32500,6 +32554,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 +32774,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"
@@ -36910,23 +36986,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"
@@ -115702,23 +115761,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/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 501be01797e5..51a8ed2d8db1 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -864,13 +864,24 @@ public class Activity extends ContextThemeWrapper
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 @@ public class Activity extends ContextThemeWrapper
}
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/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index fa3d5c2ad8a6..23daf12f7ba3 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -1519,14 +1519,16 @@ class ApplicationContext extends Context {
// 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 +1776,6 @@ class ApplicationContext extends Context {
}
@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/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 0c8f95d7cf55..6b172363296e 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -151,6 +151,11 @@ public class ApplicationErrorReport implements Parcelable {
public String exceptionClassName;
/**
+ * Message stored in the exception.
+ */
+ public String exceptionMessage;
+
+ /**
* File which the exception was thrown from.
*/
public String throwFileName;
@@ -181,6 +186,7 @@ public class ApplicationErrorReport implements Parcelable {
*/
public CrashInfo(Parcel in) {
exceptionClassName = in.readString();
+ exceptionMessage = in.readString();
throwFileName = in.readString();
throwClassName = in.readString();
throwMethodName = in.readString();
@@ -192,6 +198,7 @@ public class ApplicationErrorReport implements Parcelable {
*/
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(exceptionClassName);
+ dest.writeString(exceptionMessage);
dest.writeString(throwFileName);
dest.writeString(throwClassName);
dest.writeString(throwMethodName);
@@ -203,6 +210,7 @@ public class ApplicationErrorReport implements Parcelable {
*/
public void dump(Printer pw, String prefix) {
pw.println(prefix + "exceptionClassName: " + exceptionClassName);
+ pw.println(prefix + "exceptionMessage: " + exceptionMessage);
pw.println(prefix + "throwFileName: " + throwFileName);
pw.println(prefix + "throwClassName: " + throwClassName);
pw.println(prefix + "throwMethodName: " + throwMethodName);
diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java
index 997bfdc1965d..85c001c097c6 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.ParcelFileDescriptor;
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 @@ import android.util.Log;
* @hide pending API solidification
*/
public abstract class BackupAgent extends ContextWrapper {
+ private static final String TAG = "BackupAgent";
+
public BackupAgent() {
super(null);
}
@@ -77,8 +82,8 @@ public abstract class BackupAgent extends ContextWrapper {
* 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 @@ public abstract class BackupAgent extends ContextWrapper {
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 @@ public abstract class BackupAgent extends ContextWrapper {
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 bf5cb5d41955..89becf40a659 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 @@ public class FullBackupAgent extends BackupAgent {
}
@Override
- public void onRestore(ParcelFileDescriptor data, ParcelFileDescriptor newState) {
+ public void onRestore(BackupDataInput data, ParcelFileDescriptor newState) {
}
}
diff --git a/core/java/android/backup/BackupDataInputStream.java b/core/java/android/backup/BackupDataInputStream.java
index 52b16754df9e..b705c4c36022 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 1348d8108980..a6d5becd3e82 100644
--- a/core/java/android/backup/BackupDataOutput.java
+++ b/core/java/android/backup/BackupDataOutput.java
@@ -24,13 +24,11 @@ import java.io.IOException;
/** @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) {
diff --git a/core/java/android/backup/FileRestoreHelper.java b/core/java/android/backup/FileRestoreHelper.java
new file mode 100644
index 000000000000..b7e36259dc9e
--- /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 ee8beddb4b23..ac7d8ee8a790 100644
--- a/core/java/android/backup/RestoreHelper.java
+++ b/core/java/android/backup/RestoreHelper.java
@@ -26,6 +26,6 @@ public interface RestoreHelper {
* 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);
}
diff --git a/core/java/android/backup/RestoreHelperBase.java b/core/java/android/backup/RestoreHelperBase.java
new file mode 100644
index 000000000000..894c9afee277
--- /dev/null
+++ b/core/java/android/backup/RestoreHelperBase.java
@@ -0,0 +1,85 @@
+/*
+ * 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.InputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+class RestoreHelperBase {
+ private static final String TAG = "RestoreHelperBase";
+ private static final int BUF_SIZE = 8 * 1024;
+
+ Context mContext;
+ byte[] mBuf = new byte[BUF_SIZE];
+ boolean mExceptionLogged;
+
+ RestoreHelperBase(Context context) {
+ mContext = context;
+ }
+
+ void writeFile(File f, InputStream in) {
+ boolean success = false;
+ FileOutputStream out = null;
+ try {
+ // Create the enclosing directory.
+ File parent = f.getParentFile();
+ parent.mkdirs();
+
+ // Copy the file.
+ int sum = 0;
+ out = new FileOutputStream(f);
+ byte[] buf = mBuf;
+ int amt;
+ while ((amt = in.read(buf)) > 0) {
+ out.write(buf, 0, amt);
+ sum += amt;
+ }
+
+ // TODO: Set the permissions of the file.
+
+ // We're done
+ success = true;
+ out = null;
+ } catch (IOException ex) {
+ // Bail on this entity. Only log one exception per helper object.
+ if (!mExceptionLogged) {
+ Log.e(TAG, "Failed restoring file '" + f + "' for app '"
+ + mContext.getPackageName() + '\'', ex);
+ mExceptionLogged = true;
+ }
+ }
+ finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException ex) {
+ }
+ }
+ if (!success) {
+ // Something didn't work out, delete the file
+ f.delete();
+ }
+ }
+ }
+}
+
+
diff --git a/core/java/android/backup/RestoreHelperDispatcher.java b/core/java/android/backup/RestoreHelperDispatcher.java
index cbfefdcc3f44..5928914ee217 100644
--- a/core/java/android/backup/RestoreHelperDispatcher.java
+++ b/core/java/android/backup/RestoreHelperDispatcher.java
@@ -16,20 +16,27 @@
package android.backup;
+import android.util.Log;
+
import java.io.IOException;
import java.util.HashMap;
/** @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 {
+ boolean alreadyComplained = false;
+
BackupDataInputStream stream = new BackupDataInputStream(input);
while (input.readNextHeader()) {
+
String rawKey = input.getKey();
int pos = rawKey.indexOf(':');
if (pos > 0) {
@@ -38,7 +45,17 @@ public class RestoreHelperDispatcher {
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.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d4a78151908e..17fcb911ed16 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2040,10 +2040,25 @@ public class Intent implements Parcelable {
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 +2079,7 @@ public class Intent implements Parcelable {
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 +2099,7 @@ public class Intent implements Parcelable {
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 +2200,50 @@ public class Intent implements Parcelable {
}
/**
+ * 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 +2255,15 @@ public class Intent implements Parcelable {
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 +2285,24 @@ public class Intent implements Parcelable {
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 +2323,23 @@ public class Intent implements Parcelable {
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 +3153,20 @@ public class Intent implements Parcelable {
}
/**
+ * 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 +3201,9 @@ public class Intent implements Parcelable {
* <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 +4175,27 @@ public class Intent implements Parcelable {
}
/**
+ * (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 +4307,12 @@ public class Intent implements Parcelable {
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 +4323,15 @@ public class Intent implements Parcelable {
* <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 +4347,39 @@ public class Intent implements Parcelable {
* @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 +4494,17 @@ public class Intent implements Parcelable {
}
}
}
+ 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 +4550,9 @@ public class Intent implements Parcelable {
if (mType != null) {
code += mType.hashCode();
}
+ if (mPackage != null) {
+ code += mPackage.hashCode();
+ }
if (mComponent != null) {
code += mComponent.hashCode();
}
@@ -4488,6 +4623,13 @@ public class Intent implements Parcelable {
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 +4646,87 @@ public class Intent implements Parcelable {
}
}
+ /**
+ * 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 +4768,7 @@ public class Intent implements Parcelable {
Uri.writeToParcel(out, mData);
out.writeString(mType);
out.writeInt(mFlags);
+ out.writeString(mPackage);
ComponentName.writeToParcel(mComponent, out);
if (mCategories != null) {
@@ -4600,6 +4802,7 @@ public class Intent implements Parcelable {
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/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1a0f31f2cafe..5656b6bdc381 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -82,9 +82,6 @@ interface IPackageManager {
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);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f74f3c2a5a14..f746a400af4a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -986,23 +986,6 @@ public abstract class PackageManager {
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/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 585ce3d2f08a..8f1b0ee066fd 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -633,15 +633,19 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
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 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
* 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 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
mPopup.showAsDropDown(getDropDownAnchorView(),
mDropDownHorizontalOffset, mDropDownVerticalOffset);
mDropDownList.setSelection(ListView.INVALID_POSITION);
- mDropDownList.hideSelector();
- mDropDownList.requestFocus();
+ clearListSelection();
post(mHideSelector);
}
}
@@ -1123,6 +1129,18 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
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 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
private class ListSelectorHider implements Runnable {
public void run() {
- if (mDropDownList != null) {
- mDropDownList.hideSelector();
- mDropDownList.requestLayout();
- }
+ clearListSelection();
}
}
@@ -1276,6 +1291,36 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
* 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 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
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 a195ac706afc..6532125d2b0a 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -174,6 +174,8 @@ public class ListView extends AbsListView {
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 c089c23bdfcf..000000000000
--- 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 123c072614ef..5caa0158d134 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.ParcelFileDescriptor;
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;
@@ -79,7 +81,10 @@ public class LocalTransport extends IBackupTransport.Stub {
while (changeSet.readNextHeader()) {
String key = changeSet.getKey();
int dataSize = changeSet.getDataSize();
- if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize);
+
+ String base64Key = new String(Base64.encode(key.getBytes()));
+ if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize
+ + " key64=" + base64Key);
if (dataSize > bufSize) {
bufSize = dataSize;
buf = new byte[bufSize];
@@ -87,7 +92,7 @@ public class LocalTransport extends IBackupTransport.Stub {
changeSet.readEntityData(buf, 0, dataSize);
if (DEBUG) Log.v(TAG, " + data size " + dataSize);
- File entityFile = new File(packageDir, key);
+ File entityFile = new File(packageDir, base64Key);
FileOutputStream entity = new FileOutputStream(entityFile);
try {
entity.write(buf, 0, dataSize);
@@ -160,7 +165,7 @@ public class LocalTransport extends IBackupTransport.Stub {
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);
diff --git a/core/jni/android_bluetooth_Database.cpp b/core/jni/android_bluetooth_Database.cpp
index 136c9a328d55..73b8efd3e3f9 100644
--- a/core/jni/android_bluetooth_Database.cpp
+++ b/core/jni/android_bluetooth_Database.cpp
@@ -53,6 +53,7 @@ static void initializeNativeDataNative(JNIEnv* env, jobject object) {
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 fe94642e258f..91a8e8e60353 100644
--- a/core/jni/android_server_BluetoothA2dpService.cpp
+++ b/core/jni/android_server_BluetoothA2dpService.cpp
@@ -84,6 +84,7 @@ static bool initNative(JNIEnv* env, jobject object) {
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 b6e979811e26..58ae4f60a78d 100644
--- a/core/jni/android_server_BluetoothDeviceService.cpp
+++ b/core/jni/android_server_BluetoothDeviceService.cpp
@@ -109,6 +109,7 @@ static bool initializeNativeDataNative(JNIEnv* env, jobject object) {
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 7c5da5bdb564..ff8f28a5f5dd 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -132,6 +132,7 @@ static void initializeNativeDataNative(JNIEnv* env, jobject object) {
LOGE("%s: Could not get onto the system bus!", __FUNCTION__);
dbus_error_free(&err);
}
+ dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
}
#endif
}
diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h
index 3ca8ad2d5c6b..fa7f8d5cf827 100644
--- a/include/utils/BackupHelpers.h
+++ b/include/utils/BackupHelpers.h
@@ -78,7 +78,7 @@ public:
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();
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
index 16ff1e55e112..34b37edd28a0 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/utils/BackupData.cpp
@@ -281,16 +281,16 @@ BackupDataReader::SkipEntityData()
}
}
-status_t
+ssize_t
BackupDataReader::ReadEntityData(void* data, size_t size)
{
if (m_status != NO_ERROR) {
return m_status;
}
int remaining = m_dataEndPos - m_pos;
+ //LOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n",
+ // size, m_pos, m_dataEndPos, remaining);
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;
}
if (remaining <= 0) {
@@ -299,7 +299,7 @@ BackupDataReader::ReadEntityData(void* data, size_t size)
int amt = read(m_fd, data, size);
CHECK_SIZE(amt, (int)size);
m_pos += size;
- return NO_ERROR;
+ return amt;
}
status_t
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index 16c66a6b681a..2407d877a180 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -3,7 +3,7 @@
android:sharedUserId="android.uid.system">
<application android:allowClearUserData="false"
- android:label="Settings Storage"
+ android:label="@string/app_label"
android:icon="@drawable/ic_launcher_settings">
<provider android:name="SettingsProvider" android:authorities="settings"
diff --git a/packages/SettingsProvider/res/values/strings.xml b/packages/SettingsProvider/res/values/strings.xml
new file mode 100644
index 000000000000..9ca575e30ea9
--- /dev/null
+++ b/packages/SettingsProvider/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<resources>
+ <!-- Name of the activity for Settings storage. -->
+ <string name="app_label">Settings Storage</string>
+</resources>
diff --git a/packages/SubscribedFeedsProvider/AndroidManifest.xml b/packages/SubscribedFeedsProvider/AndroidManifest.xml
index 6ecda48a7cb5..ca00a9b5999f 100644
--- a/packages/SubscribedFeedsProvider/AndroidManifest.xml
+++ b/packages/SubscribedFeedsProvider/AndroidManifest.xml
@@ -10,7 +10,7 @@
<application android:process="system"
android:allowClearUserData="false"
android:icon="@drawable/app_icon"
- android:label="Sync Feeds">
+ android:label="@string/app_label">
<uses-library android:name="com.google.android.gtalkservice" />
<provider android:name="SubscribedFeedsProvider"
android:authorities="subscribedfeeds" android:syncable="false"
diff --git a/packages/SubscribedFeedsProvider/res/values/strings.xml b/packages/SubscribedFeedsProvider/res/values/strings.xml
new file mode 100644
index 000000000000..072571d034b5
--- /dev/null
+++ b/packages/SubscribedFeedsProvider/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <!-- Title of the feed synchronization activity. -->
+ <string name="app_label">Sync Feeds</string>
+</resources>
+
diff --git a/packages/VpnServices/AndroidManifest.xml b/packages/VpnServices/AndroidManifest.xml
index e48b2da6ea46..6092e302c9bf 100644
--- a/packages/VpnServices/AndroidManifest.xml
+++ b/packages/VpnServices/AndroidManifest.xml
@@ -3,7 +3,7 @@
package="com.android.server.vpn"
android:sharedUserId="android.uid.system"
>
- <application android:label="VPN Services">
+ <application android:label="@string/app_label">
<service android:name=".VpnServiceBinder" android:process=":remote">
<intent-filter>
diff --git a/packages/VpnServices/res/values/strings.xml b/packages/VpnServices/res/values/strings.xml
index 892850a35407..e42c1e87efb6 100755
--- a/packages/VpnServices/res/values/strings.xml
+++ b/packages/VpnServices/res/values/strings.xml
@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+ <!-- Title for the VPN Services activity. -->
+ <string name="app_label">VPN Services</string>
+
<string name="vpn_notification_title">%s VPN %s</string>
<string name="vpn_notification_connected">connected</string>
<string name="vpn_notification_disconnected">disconnected</string>
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index d842d341fa35..231582104cc8 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -21,14 +21,16 @@ import android.app.IActivityManager;
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.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -48,7 +50,6 @@ import android.backup.BackupManager;
import android.backup.RestoreSet;
import com.android.internal.backup.LocalTransport;
-import com.android.internal.backup.GoogleTransport;
import com.android.internal.backup.IBackupTransport;
import java.io.EOFException;
@@ -122,7 +123,7 @@ class BackupManagerService extends IBackupManager.Stub {
// Current active transport & restore session
private int mTransportId;
- private IBackupTransport mTransport;
+ private IBackupTransport mLocalTransport, mGoogleTransport;
private RestoreSession mActiveRestoreSession;
private File mStateDir;
@@ -151,10 +152,18 @@ class BackupManagerService extends IBackupManager.Stub {
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_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 +258,19 @@ class BackupManagerService extends IBackupManager.Stub {
}
};
+ // ----- 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 +278,13 @@ class BackupManagerService extends IBackupManager.Stub {
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 +317,10 @@ class BackupManagerService extends IBackupManager.Stub {
// 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 +329,7 @@ class BackupManagerService extends IBackupManager.Stub {
{
int token = msg.arg1;
IBackupTransport transport = (IBackupTransport)msg.obj;
- (new PerformRestoreThread(transport, token)).run();
+ (new PerformRestoreThread(transport, token)).start();
break;
}
}
@@ -416,25 +447,19 @@ class BackupManagerService extends IBackupManager.Stub {
addPackageParticipantsLockedInner(packageName, allApps);
}
- // Instantiate the given transport
- private IBackupTransport createTransport(int transportID) {
- IBackupTransport transport = null;
+ // 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;
+ 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;
+ 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
@@ -937,14 +962,8 @@ class BackupManagerService extends IBackupManager.Stub {
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;
return prevTransport;
}
@@ -1005,7 +1024,7 @@ class BackupManagerService extends IBackupManager.Stub {
RestoreSet[] mRestoreSets = null;
RestoreSession(int transportID) {
- mRestoreTransport = createTransport(transportID);
+ mRestoreTransport = getTransport(transportID);
}
// --- Binder interface ---
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 73478e4b2432..f51f3d02e4d1 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -1269,28 +1269,6 @@ class PackageManagerService extends IPackageManager.Stub {
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 +1387,17 @@ class PackageManagerService extends IPackageManager.Stub {
}
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 +1564,30 @@ class PackageManagerService extends IPackageManager.Stub {
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 +1620,17 @@ class PackageManagerService extends IPackageManager.Stub {
}
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;
}
}
@@ -3106,6 +3124,27 @@ class PackageManagerService extends IPackageManager.Stub {
(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/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index ae867fb75537..045e63611456 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -7904,7 +7904,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// 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;
}
@@ -8218,12 +8219,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
report.time = crashData.getTime();
report.crashInfo.stackTrace = throwData.toString();
- // extract the source of the exception, useful for report
- // clustering
+ // Extract the source of the exception, useful for report
+ // clustering. Also extract the "deepest" non-null exception
+ // message.
+ String exceptionMessage = throwData.getMessage();
while (throwData.getCause() != null) {
throwData = throwData.getCause();
+ String msg = throwData.getMessage();
+ if (msg != null && msg.length() > 0) {
+ exceptionMessage = msg;
+ }
}
StackTraceElementData trace = throwData.getStackTrace()[0];
+ report.crashInfo.exceptionMessage = exceptionMessage;
report.crashInfo.exceptionClassName = throwData.getType();
report.crashInfo.throwFileName = trace.getFileName();
report.crashInfo.throwClassName = trace.getClassName();
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 866334b2d28e..2d58659dfd3b 100755
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -56,9 +56,9 @@ public final class UsageStatsService extends IUsageStats.Stub {
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 @@ public final class UsageStatsService extends IUsageStats.Stub {
// 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 @@ public final class UsageStatsService extends IUsageStats.Stub {
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 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
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 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
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 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
}
- void updateResume() {
- mLaunchCount ++;
+ void updateResume(boolean launched) {
+ if (launched) {
+ mLaunchCount ++;
+ }
mResumedTime = SystemClock.elapsedRealtime();
}
@@ -162,6 +173,15 @@ public final class UsageStatsService extends IUsageStats.Stub {
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 final class UsageStatsService extends IUsageStats.Stub {
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 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
pus.updatePause();
}
+
+ // Persist current data to file if needed.
+ writeStatsToFile(false);
}
public void noteLaunchTime(ComponentName componentName, int millis) {
@@ -631,9 +681,9 @@ public final class UsageStatsService extends IUsageStats.Stub {
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 @@ public final class UsageStatsService extends IUsageStats.Stub {
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 @@ public final class UsageStatsService extends IUsageStats.Stub {
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/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index 5e7802d2994d..399a0536f2b2 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -63,11 +63,6 @@ public class MockPackageManager extends PackageManager {
}
@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 af7dfd4c1915..d87e85c51d6c 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.ListView;
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,43 @@ public class BackupTestActivity extends ListActivity
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());
+ dispatch.dispatch(data);
+ dataFile.close();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
}
};
@@ -154,5 +198,14 @@ public class BackupTestActivity extends ListActivity
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 a370d697fb48..e3566ec33564 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);
}
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java
new file mode 100644
index 000000000000..273943fe2d2e
--- /dev/null
+++ b/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.framework.permission.tests;
+
+import java.util.ArrayList;
+
+import android.telephony.SmsManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Verify that SmsManager apis cannot be called without required permissions.
+ */
+public class SmsManagerPermissionTest extends AndroidTestCase {
+
+ private static final String MSG_CONTENTS = "hi";
+ private static final short DEST_PORT = (short)1004;
+ private static final String DEST_NUMBER = "4567";
+ private static final String SRC_NUMBER = "1234";
+
+ /**
+ * Verify that SmsManager.sendTextMessage requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#SEND_SMS}.
+ */
+ @SmallTest
+ public void testSendTextMessage() {
+ try {
+ SmsManager.getDefault().sendTextMessage(SRC_NUMBER, DEST_NUMBER, MSG_CONTENTS, null,
+ null);
+ fail("SmsManager.sendTextMessage did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that SmsManager.sendDataMessage requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#SEND_SMS}.
+ */
+ @SmallTest
+ public void testSendDataMessage() {
+ try {
+ SmsManager.getDefault().sendDataMessage(SRC_NUMBER, DEST_NUMBER, DEST_PORT,
+ MSG_CONTENTS.getBytes(), null, null);
+ fail("SmsManager.sendDataMessage did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Verify that SmsManager.sendMultipartMessage requires permissions.
+ * <p>Tests Permission:
+ * {@link android.Manifest.permission#SEND_MMS}.
+ */
+ @SmallTest
+ public void testSendMultipartMessage() {
+ try {
+ ArrayList<String> msgParts = new ArrayList<String>(2);
+ msgParts.add(MSG_CONTENTS);
+ msgParts.add("foo");
+ SmsManager.getDefault().sendMultipartTextMessage(SRC_NUMBER, DEST_NUMBER, msgParts,
+ null, null);
+ fail("SmsManager.sendMultipartTextMessage did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+}