diff options
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 + } + } +} |