Merge "In permission review mode, always request user's consent to toggle BT."
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index d4dcacc..0c4573b 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -391,7 +391,7 @@
     public ContextHubManager(Context context, Looper mainLooper) {
         mMainLooper = mainLooper;
 
-        IBinder b = ServiceManager.getService(ContextHubService.CONTEXTHUB_SERVICE);
+        IBinder b = ServiceManager.getService(Context.CONTEXTHUB_SERVICE);
         if (b != null) {
             mContextHubService = IContextHubService.Stub.asInterface(b);
 
diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java
index 83866b3..e617e0a 100644
--- a/core/java/android/os/HwRemoteBinder.java
+++ b/core/java/android/os/HwRemoteBinder.java
@@ -39,6 +39,9 @@
     public native final void transact(
             int code, HwParcel request, HwParcel reply, int flags);
 
+    public native boolean linkToDeath(DeathRecipient recipient, long cookie);
+    public native boolean unlinkToDeath(DeathRecipient recipient);
+
     private static native final long native_init();
 
     private native final void native_setup_empty();
@@ -52,5 +55,9 @@
                 128 /* size */);
     }
 
+    private static final void sendDeathNotice(DeathRecipient recipient, long cookie) {
+        recipient.serviceDied(cookie);
+    }
+
     private long mNativeContext;
 }
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index 76e881e..f93bfd7 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -26,4 +26,16 @@
             int code, HwParcel request, HwParcel reply, int flags);
 
     public IHwInterface queryLocalInterface(String descriptor);
+
+    /**
+     * Interface for receiving a callback when the process hosting a service
+     * has gone away.
+     */
+    public interface DeathRecipient {
+        public void serviceDied(long cookie);
+    }
+
+    public boolean linkToDeath(DeathRecipient recipient, long cookie);
+
+    public boolean unlinkToDeath(DeathRecipient recipient);
 }
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index 1d5d6d5..0a7d84d 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -25,6 +25,7 @@
 #include <JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <hidl/Status.h>
+#include <ScopedUtfChars.h>
 #include <nativehelper/ScopedLocalRef.h>
 
 #include "core_jni_helpers.h"
@@ -38,26 +39,196 @@
 namespace android {
 
 static struct fields_t {
+    jclass proxy_class;
     jfieldID contextID;
     jmethodID constructID;
+    jmethodID sendDeathNotice;
+} gProxyOffsets;
 
-} gFields;
+static struct class_offsets_t
+{
+    jmethodID mGetName;
+} gClassOffsets;
+
+static JavaVM* jnienv_to_javavm(JNIEnv* env)
+{
+    JavaVM* vm;
+    return env->GetJavaVM(&vm) >= 0 ? vm : NULL;
+}
+
+static JNIEnv* javavm_to_jnienv(JavaVM* vm)
+{
+    JNIEnv* env;
+    return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL;
+}
+
+// ----------------------------------------------------------------------------
+class HwBinderDeathRecipient : public hardware::IBinder::DeathRecipient
+{
+public:
+    HwBinderDeathRecipient(JNIEnv* env, jobject object, jlong cookie, const sp<HwBinderDeathRecipientList>& list)
+        : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)),
+          mObjectWeak(NULL), mCookie(cookie), mList(list)
+    {
+        // These objects manage their own lifetimes so are responsible for final bookkeeping.
+        // The list holds a strong reference to this object.
+        list->add(this);
+    }
+
+    void binderDied(const wp<hardware::IBinder>& who)
+    {
+        if (mObject != NULL) {
+            JNIEnv* env = javavm_to_jnienv(mVM);
+
+            env->CallStaticVoidMethod(gProxyOffsets.proxy_class, gProxyOffsets.sendDeathNotice, mObject, mCookie);
+            if (env->ExceptionCheck()) {
+                ALOGE("Uncaught exception returned from death notification.");
+                env->ExceptionClear();
+            }
+
+            // Serialize with our containing HwBinderDeathRecipientList so that we can't
+            // delete the global ref on mObject while the list is being iterated.
+            sp<HwBinderDeathRecipientList> list = mList.promote();
+            if (list != NULL) {
+                AutoMutex _l(list->lock());
+
+                // Demote from strong ref to weak after binderDied() has been delivered,
+                // to allow the DeathRecipient and BinderProxy to be GC'd if no longer needed.
+                mObjectWeak = env->NewWeakGlobalRef(mObject);
+                env->DeleteGlobalRef(mObject);
+                mObject = NULL;
+            }
+        }
+    }
+
+    void clearReference()
+    {
+        sp<HwBinderDeathRecipientList> list = mList.promote();
+        if (list != NULL) {
+            list->remove(this);
+        } else {
+            ALOGE("clearReference() on JDR %p but DRL wp purged", this);
+        }
+    }
+
+    bool matches(jobject obj) {
+        bool result;
+        JNIEnv* env = javavm_to_jnienv(mVM);
+
+        if (mObject != NULL) {
+            result = env->IsSameObject(obj, mObject);
+        } else {
+            jobject me = env->NewLocalRef(mObjectWeak);
+            result = env->IsSameObject(obj, me);
+            env->DeleteLocalRef(me);
+        }
+        return result;
+    }
+
+    void warnIfStillLive() {
+        if (mObject != NULL) {
+            // Okay, something is wrong -- we have a hard reference to a live death
+            // recipient on the VM side, but the list is being torn down.
+            JNIEnv* env = javavm_to_jnienv(mVM);
+            ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject));
+            ScopedLocalRef<jstring> nameRef(env,
+                    (jstring) env->CallObjectMethod(objClassRef.get(), gClassOffsets.mGetName));
+            ScopedUtfChars nameUtf(env, nameRef.get());
+            if (nameUtf.c_str() != NULL) {
+                ALOGW("BinderProxy is being destroyed but the application did not call "
+                        "unlinkToDeath to unlink all of its death recipients beforehand.  "
+                        "Releasing leaked death recipient: %s", nameUtf.c_str());
+            } else {
+                ALOGW("BinderProxy being destroyed; unable to get DR object name");
+                env->ExceptionClear();
+            }
+        }
+    }
+
+protected:
+    virtual ~HwBinderDeathRecipient()
+    {
+        JNIEnv* env = javavm_to_jnienv(mVM);
+        if (mObject != NULL) {
+            env->DeleteGlobalRef(mObject);
+        } else {
+            env->DeleteWeakGlobalRef(mObjectWeak);
+        }
+    }
+
+private:
+    JavaVM* const mVM;
+    jobject mObject;
+    jweak mObjectWeak; // will be a weak ref to the same VM-side DeathRecipient after binderDied()
+    jlong mCookie;
+    wp<HwBinderDeathRecipientList> mList;
+};
+// ----------------------------------------------------------------------------
+
+HwBinderDeathRecipientList::HwBinderDeathRecipientList() {
+}
+
+HwBinderDeathRecipientList::~HwBinderDeathRecipientList() {
+    AutoMutex _l(mLock);
+
+    for (const sp<HwBinderDeathRecipient>& deathRecipient : mList) {
+        deathRecipient->warnIfStillLive();
+    }
+}
+
+void HwBinderDeathRecipientList::add(const sp<HwBinderDeathRecipient>& recipient) {
+    AutoMutex _l(mLock);
+
+    mList.push_back(recipient);
+}
+
+void HwBinderDeathRecipientList::remove(const sp<HwBinderDeathRecipient>& recipient) {
+    AutoMutex _l(mLock);
+
+    List< sp<HwBinderDeathRecipient> >::iterator iter;
+    for (iter = mList.begin(); iter != mList.end(); iter++) {
+        if (*iter == recipient) {
+            mList.erase(iter);
+            return;
+        }
+    }
+}
+
+sp<HwBinderDeathRecipient> HwBinderDeathRecipientList::find(jobject recipient) {
+    AutoMutex _l(mLock);
+
+    for (const sp<HwBinderDeathRecipient>& deathRecipient : mList) {
+        if (deathRecipient->matches(recipient)) {
+            return deathRecipient;
+        }
+    }
+    return NULL;
+}
+
+Mutex& HwBinderDeathRecipientList::lock() {
+    return mLock;
+}
 
 // static
 void JHwRemoteBinder::InitClass(JNIEnv *env) {
-    ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
+    jclass clazz = FindClassOrDie(env, CLASS_PATH);
 
-    gFields.contextID =
-        GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J");
+    gProxyOffsets.proxy_class = MakeGlobalRefOrDie(env, clazz);
+    gProxyOffsets.contextID =
+        GetFieldIDOrDie(env, clazz, "mNativeContext", "J");
+    gProxyOffsets.constructID = GetMethodIDOrDie(env, clazz, "<init>", "()V");
+    gProxyOffsets.sendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice",
+            "(Landroid/os/IHwBinder$DeathRecipient;J)V");
 
-    gFields.constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V");
+    clazz = FindClassOrDie(env, "java/lang/Class");
+    gClassOffsets.mGetName = GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");
 }
 
 // static
 sp<JHwRemoteBinder> JHwRemoteBinder::SetNativeContext(
         JNIEnv *env, jobject thiz, const sp<JHwRemoteBinder> &context) {
     sp<JHwRemoteBinder> old =
-        (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID);
+        (JHwRemoteBinder *)env->GetLongField(thiz, gProxyOffsets.contextID);
 
     if (context != NULL) {
         context->incStrong(NULL /* id */);
@@ -67,7 +238,7 @@
         old->decStrong(NULL /* id */);
     }
 
-    env->SetLongField(thiz, gFields.contextID, (long)context.get());
+    env->SetLongField(thiz, gProxyOffsets.contextID, (long)context.get());
 
     return old;
 }
@@ -75,7 +246,7 @@
 // static
 sp<JHwRemoteBinder> JHwRemoteBinder::GetNativeContext(
         JNIEnv *env, jobject thiz) {
-    return (JHwRemoteBinder *)env->GetLongField(thiz, gFields.contextID);
+    return (JHwRemoteBinder *)env->GetLongField(thiz, gProxyOffsets.contextID);
 }
 
 // static
@@ -84,7 +255,7 @@
     ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
 
     // XXX Have to look up the constructor here because otherwise that static
-    // class initializer isn't called and gFields.constructID is undefined :(
+    // class initializer isn't called and gProxyOffsets.constructID is undefined :(
 
     jmethodID constructID = GetMethodIDOrDie(env, clazz.get(), "<init>", "()V");
 
@@ -97,6 +268,7 @@
 JHwRemoteBinder::JHwRemoteBinder(
         JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder)
     : mBinder(binder) {
+    mDeathRecipientList = new HwBinderDeathRecipientList();
     jclass clazz = env->GetObjectClass(thiz);
     CHECK(clazz != NULL);
 
@@ -114,7 +286,7 @@
     mClass = NULL;
 }
 
-sp<hardware::IBinder> JHwRemoteBinder::getBinder() {
+sp<hardware::IBinder> JHwRemoteBinder::getBinder() const {
     return mBinder;
 }
 
@@ -122,6 +294,10 @@
     mBinder = binder;
 }
 
+sp<HwBinderDeathRecipientList> JHwRemoteBinder::getDeathRecipientList() const {
+    return mDeathRecipientList;
+}
+
 }  // namespace android
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -174,6 +350,73 @@
     signalExceptionForError(env, err);
 }
 
+static jboolean JHwRemoteBinder_linkToDeath(JNIEnv* env, jobject thiz,
+        jobject recipient, jlong cookie)
+{
+    if (recipient == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return JNI_FALSE;
+    }
+
+    sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, thiz);
+    sp<hardware::IBinder> binder = context->getBinder();
+
+    if (!binder->localBinder()) {
+        HwBinderDeathRecipientList* list = (context->getDeathRecipientList()).get();
+        sp<HwBinderDeathRecipient> jdr = new HwBinderDeathRecipient(env, recipient, cookie, list);
+        status_t err = binder->linkToDeath(jdr, NULL, 0);
+        if (err != NO_ERROR) {
+            // Failure adding the death recipient, so clear its reference
+            // now.
+            jdr->clearReference();
+            return JNI_FALSE;
+        }
+    }
+
+    return JNI_TRUE;
+}
+
+static jboolean JHwRemoteBinder_unlinkToDeath(JNIEnv* env, jobject thiz,
+                                                 jobject recipient)
+{
+    jboolean res = JNI_FALSE;
+    if (recipient == NULL) {
+        jniThrowNullPointerException(env, NULL);
+        return res;
+    }
+
+    sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, thiz);
+    sp<hardware::IBinder> binder = context->getBinder();
+
+    if (!binder->localBinder()) {
+        status_t err = NAME_NOT_FOUND;
+
+        // If we find the matching recipient, proceed to unlink using that
+        HwBinderDeathRecipientList* list = (context->getDeathRecipientList()).get();
+        sp<HwBinderDeathRecipient> origJDR = list->find(recipient);
+        if (origJDR != NULL) {
+            wp<hardware::IBinder::DeathRecipient> dr;
+            err = binder->unlinkToDeath(origJDR, NULL, 0, &dr);
+            if (err == NO_ERROR && dr != NULL) {
+                sp<hardware::IBinder::DeathRecipient> sdr = dr.promote();
+                HwBinderDeathRecipient* jdr = static_cast<HwBinderDeathRecipient*>(sdr.get());
+                if (jdr != NULL) {
+                    jdr->clearReference();
+                }
+            }
+        }
+
+        if (err == NO_ERROR || err == DEAD_OBJECT) {
+            res = JNI_TRUE;
+        } else {
+            jniThrowException(env, "java/util/NoSuchElementException",
+                              "Death link does not exist");
+        }
+    }
+
+    return res;
+}
+
 static JNINativeMethod gMethods[] = {
     { "native_init", "()J", (void *)JHwRemoteBinder_native_init },
 
@@ -183,6 +426,14 @@
     { "transact",
         "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V",
         (void *)JHwRemoteBinder_native_transact },
+
+    {"linkToDeath",
+        "(Landroid/os/IHwBinder$DeathRecipient;J)Z",
+        (void*)JHwRemoteBinder_linkToDeath},
+
+    {"unlinkToDeath",
+        "(Landroid/os/IHwBinder$DeathRecipient;)Z",
+        (void*)JHwRemoteBinder_unlinkToDeath},
 };
 
 namespace android {
diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h
index fd33338..77a0278 100644
--- a/core/jni/android_os_HwRemoteBinder.h
+++ b/core/jni/android_os_HwRemoteBinder.h
@@ -20,10 +20,33 @@
 #include <android-base/macros.h>
 #include <hwbinder/Binder.h>
 #include <jni.h>
+#include <utils/List.h>
+#include <utils/Mutex.h>
 #include <utils/RefBase.h>
 
 namespace android {
 
+// Per-IBinder death recipient bookkeeping.  This is how we reconcile local jobject
+// death recipient references passed in through JNI with the permanent corresponding
+// HwBinderDeathRecipient objects.
+
+class HwBinderDeathRecipient;
+
+class HwBinderDeathRecipientList : public RefBase {
+    List< sp<HwBinderDeathRecipient> > mList;
+    Mutex mLock;
+
+public:
+    HwBinderDeathRecipientList();
+    ~HwBinderDeathRecipientList();
+
+    void add(const sp<HwBinderDeathRecipient>& recipient);
+    void remove(const sp<HwBinderDeathRecipient>& recipient);
+    sp<HwBinderDeathRecipient> find(jobject recipient);
+
+    Mutex& lock();  // Use with care; specifically for mutual exclusion during binder death
+};
+
 struct JHwRemoteBinder : public RefBase {
     static void InitClass(JNIEnv *env);
 
@@ -37,8 +60,9 @@
     JHwRemoteBinder(
             JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder);
 
-    sp<hardware::IBinder> getBinder();
+    sp<hardware::IBinder> getBinder() const;
     void setBinder(const sp<hardware::IBinder> &binder);
+    sp<HwBinderDeathRecipientList> getDeathRecipientList() const;
 
 protected:
     virtual ~JHwRemoteBinder();
@@ -48,7 +72,7 @@
     jobject mObject;
 
     sp<hardware::IBinder> mBinder;
-
+    sp<HwBinderDeathRecipientList> mDeathRecipientList;
     DISALLOW_COPY_AND_ASSIGN(JHwRemoteBinder);
 };
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index dfc89fa..612eba7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -345,7 +345,10 @@
 
             @Override
             public void onAnnouncementRequested(CharSequence announcement) {
-                announceForAccessibility(announcement);
+                if (announcement != null) {
+                    mHandler.obtainMessage(H.ANNOUNCE_FOR_ACCESSIBILITY, announcement)
+                            .sendToTarget();
+                }
             }
         };
         r.tile.addCallback(callback);
@@ -514,10 +517,13 @@
     private class H extends Handler {
         private static final int SHOW_DETAIL = 1;
         private static final int SET_TILE_VISIBILITY = 2;
+        private static final int ANNOUNCE_FOR_ACCESSIBILITY = 3;
         @Override
         public void handleMessage(Message msg) {
             if (msg.what == SHOW_DETAIL) {
                 handleShowDetail((Record)msg.obj, msg.arg1 != 0);
+            } else if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) {
+                announceForAccessibility((CharSequence)msg.obj);
             }
         }
     }
diff --git a/services/core/java/com/android/server/ContextHubSystemService.java b/services/core/java/com/android/server/ContextHubSystemService.java
index 1b85632..06abca9 100644
--- a/services/core/java/com/android/server/ContextHubSystemService.java
+++ b/services/core/java/com/android/server/ContextHubSystemService.java
@@ -37,7 +37,7 @@
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
             Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
-            publishBinderService(ContextHubService.CONTEXTHUB_SERVICE, mContextHubService);
+            publishBinderService(Context.CONTEXTHUB_SERVICE, mContextHubService);
         }
     }
 }
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 2c94cfb..983d039 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -147,37 +147,39 @@
         }
 
         private void evaluateBinding(String scorerPackageName, boolean forceUnbind) {
-            if (mPackagesToWatch.contains(scorerPackageName)) {
+            if (!mPackagesToWatch.contains(scorerPackageName)) {
+                // Early exit when we don't care about the package that has changed.
+                return;
+            }
+
+            if (DBG) {
+                Log.d(TAG, "Evaluating binding for: " + scorerPackageName
+                        + ", forceUnbind=" + forceUnbind);
+            }
+            final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
+            if (activeScorer == null) {
+                // Package change has invalidated a scorer, this will also unbind any service
+                // connection.
+                if (DBG) Log.d(TAG, "No active scorers available.");
+                unbindFromScoringServiceIfNeeded();
+            } else if (activeScorer.packageName.equals(scorerPackageName)) {
+                // The active scoring service changed in some way.
                 if (DBG) {
-                    Log.d(TAG, "Evaluating binding for: " + scorerPackageName
-                            + ", forceUnbind=" + forceUnbind);
-                }
-                final NetworkScorerAppData activeScorer =
-                        mNetworkScorerAppManager.getActiveScorer();
-                if (activeScorer == null) {
-                    // Package change has invalidated a scorer, this will also unbind any service
-                    // connection.
-                    if (DBG) Log.d(TAG, "No active scorers available.");
-                    unbindFromScoringServiceIfNeeded();
-                } else if (activeScorer.packageName.equals(scorerPackageName)) {
-                    // The active scoring service changed in some way.
-                    if (DBG) {
-                        Log.d(TAG, "Possible change to the active scorer: "
+                    Log.d(TAG, "Possible change to the active scorer: "
                             + activeScorer.packageName);
-                    }
-                    if (forceUnbind) {
-                        unbindFromScoringServiceIfNeeded();
-                    }
-                    bindToScoringServiceIfNeeded(activeScorer);
-                } else {
-                    // One of the scoring apps on the device has changed and we may no longer be
-                    // bound to the correct scoring app. The logic in bindToScoringServiceIfNeeded()
-                    // will sort that out to leave us bound to the most recent active scorer.
-                    if (DBG) {
-                        Log.d(TAG, "Binding to " + activeScorer.packageName + " if needed.");
-                    }
-                    bindToScoringServiceIfNeeded(activeScorer);
                 }
+                if (forceUnbind) {
+                    unbindFromScoringServiceIfNeeded();
+                }
+                bindToScoringServiceIfNeeded(activeScorer);
+            } else {
+                // One of the scoring apps on the device has changed and we may no longer be
+                // bound to the correct scoring app. The logic in bindToScoringServiceIfNeeded()
+                // will sort that out to leave us bound to the most recent active scorer.
+                if (DBG) {
+                    Log.d(TAG, "Binding to " + activeScorer.packageName + " if needed.");
+                }
+                bindToScoringServiceIfNeeded(activeScorer);
             }
         }
     }