Merge "Fix issue #3202866: system server crash"
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index b5fddfa..ac0e410 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -217,8 +217,7 @@
 
         // The rest of the 'list' options work with a restore session on the current transport
         try {
-            String curTransport = mBmgr.getCurrentTransport();
-            mRestore = mBmgr.beginRestoreSession(curTransport);
+            mRestore = mBmgr.beginRestoreSession(null, null);
             if (mRestore == null) {
                 System.err.println(BMGR_NOT_RUNNING_ERR);
                 return;
@@ -349,8 +348,7 @@
 
     private void doRestorePackage(String pkg) {
         try {
-            String curTransport = mBmgr.getCurrentTransport();
-            mRestore = mBmgr.beginRestoreSession(curTransport);
+            mRestore = mBmgr.beginRestoreSession(pkg, null);
             if (mRestore == null) {
                 System.err.println(BMGR_NOT_RUNNING_ERR);
                 return;
@@ -378,8 +376,7 @@
 
         try {
             boolean didRestore = false;
-            String curTransport = mBmgr.getCurrentTransport();
-            mRestore = mBmgr.beginRestoreSession(curTransport);
+            mRestore = mBmgr.beginRestoreSession(null, null);
             if (mRestore == null) {
                 System.err.println(BMGR_NOT_RUNNING_ERR);
                 return;
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 52dd707..80656a1 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -138,8 +138,8 @@
         if (sService != null) {
             RestoreSession session = null;
             try {
-                String transport = sService.getCurrentTransport();
-                IRestoreSession binder = sService.beginRestoreSession(transport);
+                IRestoreSession binder = sService.beginRestoreSession(mContext.getPackageName(),
+                        null);
                 session = new RestoreSession(mContext, binder);
                 result = session.restorePackage(mContext.getPackageName(), observer);
             } catch (RemoteException e) {
@@ -163,8 +163,8 @@
         checkServiceBinder();
         if (sService != null) {
             try {
-                String transport = sService.getCurrentTransport();
-                IRestoreSession binder = sService.beginRestoreSession(transport);
+                // All packages, current transport
+                IRestoreSession binder = sService.beginRestoreSession(null, null);
                 session = new RestoreSession(mContext, binder);
             } catch (RemoteException e) {
                 Log.w(TAG, "beginRestoreSession() couldn't connect");
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 23d6351..8af59df 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -144,13 +144,25 @@
     String selectBackupTransport(String transport);
 
     /**
-     * Begin a restore session with the given transport (which may differ from the
-     * currently-active backup transport).
+     * Begin a restore session.  Either or both of packageName and transportID
+     * may be null.  If packageName is non-null, then only the given package will be
+     * considered for restore.  If transportID is null, then the restore will use
+     * the current active transport.
+     * <p>
+     * This method requires the android.permission.BACKUP permission <i>except</i>
+     * when transportID is null and packageName is the name of the caller's own
+     * package.  In that case, the restore session returned is suitable for supporting
+     * the BackupManager.requestRestore() functionality via RestoreSession.restorePackage()
+     * without requiring the app to hold any special permission.
      *
-     * @param transport The name of the transport to use for the restore operation.
+     * @param packageName The name of the single package for which a restore will
+     *        be requested.  May be null, in which case all packages in the restore
+     *        set can be restored.
+     * @param transportID The name of the transport to use for the restore operation.
+     *        May be null, in which case the current active transport is used.
      * @return An interface to the restore session, or null on error.
      */
-    IRestoreSession beginRestoreSession(String transportID);
+    IRestoreSession beginRestoreSession(String packageName, String transportID);
 
     /**
      * Notify the backup manager that a BackupAgent has completed the operation
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index aefe8d8..f32ae92 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6206,7 +6206,10 @@
             if (applyTransformation) {
                 setTransformationMethod(null);
             }
-            setBackgroundDrawable(mEditTextMultilineBackground);
+            // mEditTextMultilineBackground is defined and used only in EditText
+            if (mEditTextMultilineBackground != null) {
+                setBackgroundDrawable(mEditTextMultilineBackground);
+            }
         }
     }
     
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index df03e13..20949a4 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -389,6 +389,10 @@
     synchronized void nScriptSetVarV(int id, int slot, byte[] val) {
         rsnScriptSetVarV(mContext, id, slot, val);
     }
+    native void rsnScriptSetVarObj(int con, int id, int slot, int val);
+    synchronized void nScriptSetVarObj(int id, int slot, int val) {
+        rsnScriptSetVarObj(mContext, id, slot, val);
+    }
 
     native void rsnScriptCBegin(int con);
     synchronized void nScriptCBegin() {
diff --git a/graphics/java/android/renderscript/Script.java b/graphics/java/android/renderscript/Script.java
index 7d7dd6d..ea616c6 100644
--- a/graphics/java/android/renderscript/Script.java
+++ b/graphics/java/android/renderscript/Script.java
@@ -88,6 +88,10 @@
         mRS.nScriptSetVarI(getID(), index, v ? 1 : 0);
     }
 
+    public void setVar(int index, BaseObj o) {
+        mRS.nScriptSetVarObj(getID(), index, (o == null) ? 0 : o.getID());
+    }
+
     public void setVar(int index, FieldPacker v) {
         mRS.nScriptSetVarV(getID(), index, v.getData());
     }
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index dd84848..8888459 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -829,6 +829,13 @@
 }
 
 static void
+nScriptSetVarObj(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jint val)
+{
+    LOG_API("nScriptSetVarObj, con(%p), s(%p), slot(%i), val(%i)", con, (void *)script, slot, val);
+    rsScriptSetVarObj(con, (RsScript)script, slot, (RsObjectBase)val);
+}
+
+static void
 nScriptSetVarJ(JNIEnv *_env, jobject _this, RsContext con, jint script, jint slot, jlong val)
 {
     LOG_API("nScriptSetVarJ, con(%p), s(%p), slot(%i), val(%lli)", con, (void *)script, slot, val);
@@ -1335,6 +1342,7 @@
 {"rsnScriptSetVarF",                 "(IIIF)V",                               (void*)nScriptSetVarF },
 {"rsnScriptSetVarD",                 "(IIID)V",                               (void*)nScriptSetVarD },
 {"rsnScriptSetVarV",                 "(III[B)V",                              (void*)nScriptSetVarV },
+{"rsnScriptSetVarObj",               "(IIII)V",                               (void*)nScriptSetVarObj },
 
 {"rsnScriptCBegin",                  "(I)V",                                  (void*)nScriptCBegin },
 {"rsnScriptCSetScript",              "(I[BII)V",                              (void*)nScriptCSetScript },
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index ac9abe0..76db14f 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -280,6 +280,12 @@
 	param int value
 	}
 
+ScriptSetVarObj {
+	param RsScript s
+	param uint32_t slot
+	param RsObjectBase value
+	}
+
 ScriptSetVarJ {
 	param RsScript s
 	param uint32_t slot
diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp
index 4ffdbfd..efdc626 100644
--- a/libs/rs/rsScript.cpp
+++ b/libs/rs/rsScript.cpp
@@ -67,6 +67,22 @@
     }
 }
 
+void Script::setVarObj(uint32_t slot, ObjectBase *val) {
+    ObjectBase **destPtr = ((ObjectBase ***)mEnviroment.mFieldAddress)[slot];
+
+    if (destPtr) {
+        if (val != NULL) {
+            val->incSysRef();
+        }
+        if (*destPtr) {
+            (*destPtr)->decSysRef();
+        }
+        *destPtr = val;
+    } else {
+        LOGV("Calling setVarObj on slot = %i which is null.  This is dangerous because the script will not hold a ref count on the object.", slot);
+    }
+}
+
 namespace android {
 namespace renderscript {
 
@@ -103,6 +119,12 @@
     s->setVar(slot, &value, sizeof(value));
 }
 
+void rsi_ScriptSetVarObj(Context *rsc, RsScript vs, uint32_t slot, RsObjectBase value) {
+    Script *s = static_cast<Script *>(vs);
+    ObjectBase *o = static_cast<ObjectBase *>(value);
+    s->setVarObj(slot, o);
+}
+
 void rsi_ScriptSetVarJ(Context *rsc, RsScript vs, uint32_t slot, long long value) {
     Script *s = static_cast<Script *>(vs);
     s->setVar(slot, &value, sizeof(value));
diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h
index 9b6d8a9..bad095b 100644
--- a/libs/rs/rsScript.h
+++ b/libs/rs/rsScript.h
@@ -61,6 +61,7 @@
     void initSlots();
     void setSlot(uint32_t slot, Allocation *a);
     void setVar(uint32_t slot, const void *val, uint32_t len);
+    void setVarObj(uint32_t slot, ObjectBase *val);
 
     virtual void runForEach(Context *rsc,
                             const Allocation * ain,
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 6a86076..bebd013 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -2399,15 +2399,45 @@
     }
 
     // Hand off a restore session
-    public IRestoreSession beginRestoreSession(String transport) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "beginRestoreSession");
+    public IRestoreSession beginRestoreSession(String packageName, String transport) {
+        if (DEBUG) Slog.v(TAG, "beginRestoreSession: pkg=" + packageName
+                + " transport=" + transport);
+
+        boolean needPermission = true;
+        if (transport == null) {
+            transport = mCurrentTransport;
+
+            if (packageName != null) {
+                PackageInfo app = null;
+                try {
+                    app = mPackageManager.getPackageInfo(packageName, 0);
+                } catch (NameNotFoundException nnf) {
+                    Slog.w(TAG, "Asked to restore nonexistent pkg " + packageName);
+                    throw new IllegalArgumentException("Package " + packageName + " not found");
+                }
+
+                if (app.applicationInfo.uid == Binder.getCallingUid()) {
+                    // So: using the current active transport, and the caller has asked
+                    // that its own package will be restored.  In this narrow use case
+                    // we do not require the caller to hold the permission.
+                    needPermission = false;
+                }
+            }
+        }
+
+        if (needPermission) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                    "beginRestoreSession");
+        } else {
+            if (DEBUG) Slog.d(TAG, "restoring self on current transport; no permission needed");
+        }
 
         synchronized(this) {
             if (mActiveRestoreSession != null) {
                 Slog.d(TAG, "Restore session requested but one already active");
                 return null;
             }
-            mActiveRestoreSession = new ActiveRestoreSession(transport);
+            mActiveRestoreSession = new ActiveRestoreSession(packageName, transport);
         }
         return mActiveRestoreSession;
     }
@@ -2427,10 +2457,12 @@
     class ActiveRestoreSession extends IRestoreSession.Stub {
         private static final String TAG = "RestoreSession";
 
+        private String mPackageName;
         private IBackupTransport mRestoreTransport = null;
         RestoreSet[] mRestoreSets = null;
 
-        ActiveRestoreSession(String transport) {
+        ActiveRestoreSession(String packageName, String transport) {
+            mPackageName = packageName;
             mRestoreTransport = getTransport(transport);
         }
 
@@ -2466,11 +2498,16 @@
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                     "performRestore");
 
-            if (DEBUG) Slog.d(TAG, "performRestore token=" + Long.toHexString(token)
+            if (DEBUG) Slog.d(TAG, "restoreAll token=" + Long.toHexString(token)
                     + " observer=" + observer);
 
             if (mRestoreTransport == null || mRestoreSets == null) {
-                Slog.e(TAG, "Ignoring performRestore() with no restore set");
+                Slog.e(TAG, "Ignoring restoreAll() with no restore set");
+                return -1;
+            }
+
+            if (mPackageName != null) {
+                Slog.e(TAG, "Ignoring restoreAll() on single-package session");
                 return -1;
             }
 
@@ -2495,6 +2532,14 @@
         public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
             if (DEBUG) Slog.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
 
+            if (mPackageName != null) {
+                if (! mPackageName.equals(packageName)) {
+                    Slog.e(TAG, "Ignoring attempt to restore pkg=" + packageName
+                            + " on session for package " + mPackageName);
+                    return -1;
+                }
+            }
+
             PackageInfo app = null;
             try {
                 app = mPackageManager.getPackageInfo(packageName, 0);
@@ -2529,6 +2574,7 @@
             // the app has never been backed up from this device -- there's nothing
             // to do but return failure.
             if (token == 0) {
+                if (DEBUG) Slog.w(TAG, "No data available for this package; not restoring");
                 return -1;
             }
 
@@ -2543,9 +2589,6 @@
         }
 
         public synchronized void endRestoreSession() {
-            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                    "endRestoreSession");
-
             if (DEBUG) Slog.d(TAG, "endRestoreSession");
 
             synchronized (this) {