Merge "Remove code duplication in InputMethodManagerService"
diff --git a/api/current.txt b/api/current.txt
index e669bf1..8a5f1df 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5174,8 +5174,6 @@
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
     method public android.os.UserHandle createUser(android.content.ComponentName, java.lang.String);
-    method public void enableSystemApp(android.content.ComponentName, java.lang.String);
-    method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
@@ -26751,6 +26749,7 @@
     method public void disconnect();
     method public android.speech.tts.TextToSpeechClient.EngineStatus getEngineStatus();
     method public boolean isConnected();
+    method public boolean isSpeaking();
     method public void queueAudio(android.net.Uri, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
     method public void queueSilence(long, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.TextToSpeechClient.RequestCallbacks);
     method public void queueSpeak(java.lang.String, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index afe2981..24a354f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2170,45 +2170,6 @@
     }
 
     /**
-     * Called by profile or device owner to re-enable a system app that was disabled by default
-     * when the managed profile was created. This should only be called from a profile or device
-     * owner running within a managed profile.
-     *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param packageName The package to be re-enabled in the current profile.
-     */
-    public void enableSystemApp(ComponentName admin, String packageName) {
-        if (mService != null) {
-            try {
-                mService.enableSystemApp(admin, packageName);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to install package: " + packageName);
-            }
-        }
-    }
-
-    /**
-     * Called by profile or device owner to re-enable system apps by intent that were disabled
-     * by default when the managed profile was created. This should only be called from a profile
-     * or device owner running within a managed profile.
-     *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param intent An intent matching the app(s) to be installed. All apps that resolve for this
-     *               intent will be re-enabled in the current profile.
-     * @return int The number of activities that matched the intent and were installed.
-     */
-    public int enableSystemApp(ComponentName admin, Intent intent) {
-        if (mService != null) {
-            try {
-                return mService.enableSystemAppWithIntent(admin, intent);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to install packages matching filter: " + intent);
-            }
-        }
-        return 0;
-    }
-
-    /**
      * Called by a profile owner to disable account management for a specific type of account.
      *
      * <p>The calling device admin must be a profile owner. If it is not, a
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 7f754dc..7d7a312 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -135,9 +135,6 @@
     UserHandle createUser(in ComponentName who, in String name);
     boolean removeUser(in ComponentName who, in UserHandle userHandle);
 
-    void enableSystemApp(in ComponentName admin, in String packageName);
-    int enableSystemAppWithIntent(in ComponentName admin, in Intent intent);
-
     void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
     String[] getAccountTypesWithManagementDisabled();
 
diff --git a/core/java/android/speech/tts/TextToSpeechClient.java b/core/java/android/speech/tts/TextToSpeechClient.java
index e17b498..0c0be83 100644
--- a/core/java/android/speech/tts/TextToSpeechClient.java
+++ b/core/java/android/speech/tts/TextToSpeechClient.java
@@ -793,15 +793,14 @@
             return mService != null && mEstablished;
         }
 
-        boolean runAction(Action action) {
+        <T> ActionResult<T> runAction(Action<T> action) {
             synchronized (mLock) {
                 try {
-                    action.run(mService);
-                    return true;
+                    return new ActionResult<T>(true, action.run(mService));
                 } catch (Exception ex) {
                     Log.e(TAG, action.getName() + " failed", ex);
                     disconnect();
-                    return false;
+                    return new ActionResult<T>(false);
                 }
             }
         }
@@ -821,7 +820,7 @@
         }
     }
 
-    private abstract class Action {
+    private abstract class Action<T> {
         private final String mName;
 
         public Action(String name) {
@@ -829,7 +828,21 @@
         }
 
         public String getName() {return mName;}
-        abstract void run(ITextToSpeechService service) throws RemoteException;
+        abstract T run(ITextToSpeechService service) throws RemoteException;
+    }
+
+    private class ActionResult<T> {
+        boolean mSuccess;
+        T mResult;
+
+        ActionResult(boolean success) {
+            mSuccess = success;
+        }
+
+        ActionResult(boolean success, T result) {
+            mSuccess = success;
+            mResult = result;
+        }
     }
 
     private IBinder getCallerIdentity() {
@@ -839,18 +852,17 @@
         return null;
     }
 
-    private boolean runAction(Action action) {
+    private <T> ActionResult<T> runAction(Action<T> action) {
         synchronized (mLock) {
             if (mServiceConnection == null) {
                 Log.w(TAG, action.getName() + " failed: not bound to TTS engine");
-                return false;
+                return new ActionResult<T>(false);
             }
             if (!mServiceConnection.isEstablished()) {
                 Log.w(TAG, action.getName() + " failed: not fully bound to TTS engine");
-                return false;
+                return new ActionResult<T>(false);
             }
-            mServiceConnection.runAction(action);
-            return true;
+            return mServiceConnection.runAction(action);
         }
     }
 
@@ -861,13 +873,14 @@
      * other utterances in the queue.
      */
     public void stop() {
-        runAction(new Action(ACTION_STOP_NAME) {
+        runAction(new Action<Void>(ACTION_STOP_NAME) {
             @Override
-            public void run(ITextToSpeechService service) throws RemoteException {
+            public Void run(ITextToSpeechService service) throws RemoteException {
                if (service.stop(getCallerIdentity()) != Status.SUCCESS) {
                    Log.e(TAG, "Stop failed");
                }
                mCallbacks.clear();
+               return null;
             }
         });
     }
@@ -915,9 +928,9 @@
             final UtteranceId utteranceId,
             final RequestConfig config,
             final RequestCallbacks callbacks) {
-        runAction(new Action(ACTION_QUEUE_SPEAK_NAME) {
+        runAction(new Action<Void>(ACTION_QUEUE_SPEAK_NAME) {
             @Override
-            public void run(ITextToSpeechService service) throws RemoteException {
+            public Void run(ITextToSpeechService service) throws RemoteException {
                 RequestCallbacks c = mDefaultRequestCallbacks;
                 if (callbacks != null) {
                     c = callbacks;
@@ -925,7 +938,7 @@
                 int addCallbackStatus = addCallback(utteranceId, c);
                 if (addCallbackStatus != Status.SUCCESS) {
                     c.onSynthesisFailure(utteranceId, Status.ERROR_INVALID_REQUEST);
-                    return;
+                    return null;
                 }
 
                 int queueResult = service.speakV2(
@@ -934,6 +947,7 @@
                 if (queueResult != Status.SUCCESS) {
                     removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
                 }
+                return null;
             }
         });
     }
@@ -984,9 +998,9 @@
             final UtteranceId utteranceId,
             final File outputFile, final RequestConfig config,
             final RequestCallbacks callbacks) {
-        runAction(new Action(ACTION_QUEUE_SYNTHESIZE_TO_FILE) {
+        runAction(new Action<Void>(ACTION_QUEUE_SYNTHESIZE_TO_FILE) {
             @Override
-            public void run(ITextToSpeechService service) throws RemoteException {
+            public Void run(ITextToSpeechService service) throws RemoteException {
                 RequestCallbacks c = mDefaultRequestCallbacks;
                 if (callbacks != null) {
                     c = callbacks;
@@ -994,7 +1008,7 @@
                 int addCallbackStatus = addCallback(utteranceId, c);
                 if (addCallbackStatus != Status.SUCCESS) {
                     c.onSynthesisFailure(utteranceId, Status.ERROR_INVALID_REQUEST);
-                    return;
+                    return null;
                 }
 
                 ParcelFileDescriptor fileDescriptor = null;
@@ -1002,7 +1016,7 @@
                     if (outputFile.exists() && !outputFile.canWrite()) {
                         Log.e(TAG, "No permissions to write to " + outputFile);
                         removeCallbackAndErr(utteranceId.toUniqueString(), Status.ERROR_OUTPUT);
-                        return;
+                        return null;
                     }
                     fileDescriptor = ParcelFileDescriptor.open(outputFile,
                             ParcelFileDescriptor.MODE_WRITE_ONLY |
@@ -1023,6 +1037,7 @@
                     Log.e(TAG, "Closing file " + outputFile + " failed", e);
                     removeCallbackAndErr(utteranceId.toUniqueString(), Status.ERROR_OUTPUT);
                 }
+                return null;
             }
         });
     }
@@ -1050,9 +1065,9 @@
      */
     public void queueSilence(final long durationInMs, final UtteranceId utteranceId,
             final RequestCallbacks callbacks) {
-        runAction(new Action(ACTION_QUEUE_SILENCE_NAME) {
+        runAction(new Action<Void>(ACTION_QUEUE_SILENCE_NAME) {
             @Override
-            public void run(ITextToSpeechService service) throws RemoteException {
+            public Void run(ITextToSpeechService service) throws RemoteException {
                 RequestCallbacks c = mDefaultRequestCallbacks;
                 if (callbacks != null) {
                     c = callbacks;
@@ -1068,6 +1083,7 @@
                 if (queueResult != Status.SUCCESS) {
                     removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
                 }
+                return null;
             }
         });
     }
@@ -1091,9 +1107,9 @@
      */
     public void queueAudio(final Uri audioUrl, final UtteranceId utteranceId,
             final RequestConfig config, final RequestCallbacks callbacks) {
-        runAction(new Action(ACTION_QUEUE_AUDIO_NAME) {
+        runAction(new Action<Void>(ACTION_QUEUE_AUDIO_NAME) {
             @Override
-            public void run(ITextToSpeechService service) throws RemoteException {
+            public Void run(ITextToSpeechService service) throws RemoteException {
                 RequestCallbacks c = mDefaultRequestCallbacks;
                 if (callbacks != null) {
                     c = callbacks;
@@ -1109,10 +1125,35 @@
                 if (queueResult != Status.SUCCESS) {
                     removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
                 }
+                return null;
             }
         });
     }
 
+    private static final String ACTION_IS_SPEAKING_NAME = "isSpeaking";
+
+    /**
+     * Checks whether the TTS engine is busy speaking. Note that a speech item is
+     * considered complete once it's audio data has been sent to the audio mixer, or
+     * written to a file. There might be a finite lag between this point, and when
+     * the audio hardware completes playback.
+     *
+     * @return {@code true} if the TTS engine is speaking.
+     */
+    public boolean isSpeaking() {
+        ActionResult<Boolean> result = runAction(new Action<Boolean>(ACTION_IS_SPEAKING_NAME) {
+            @Override
+            public Boolean run(ITextToSpeechService service) throws RemoteException {
+                return service.isSpeaking();
+            }
+        });
+        if (!result.mSuccess) {
+            return false; // We can't really say, return false
+        }
+        return result.mResult;
+    }
+
+
     class InternalHandler extends Handler {
         final static int WHAT_ENGINE_STATUS_CHANGED = 1;
         final static int WHAT_SERVICE_DISCONNECTED = 2;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a52396e..043fab4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3555,111 +3555,6 @@
     }
 
     @Override
-    public void enableSystemApp(ComponentName who, String packageName) {
-        synchronized (this) {
-            if (who == null) {
-                throw new NullPointerException("ComponentName is null");
-            }
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
-            int userId = UserHandle.getCallingUserId();
-            long id = Binder.clearCallingIdentity();
-
-            try {
-                UserManager um = UserManager.get(mContext);
-                if (!um.getUserInfo(userId).isManagedProfile()) {
-                    throw new IllegalStateException(
-                            "Only call this method from a managed profile.");
-                }
-
-                // TODO: Use UserManager::getProfileParent when available.
-                UserInfo primaryUser = um.getUserInfo(UserHandle.USER_OWNER);
-
-                if (DBG) {
-                    Slog.v(LOG_TAG, "installing " + packageName + " for "
-                            + userId);
-                }
-
-                IPackageManager pm = AppGlobals.getPackageManager();
-                if (!isSystemApp(pm, packageName, primaryUser.id)) {
-                    throw new IllegalArgumentException("Only system apps can be enabled this way.");
-                }
-
-                // Install the app.
-                pm.installExistingPackageAsUser(packageName, userId);
-
-            } catch (RemoteException re) {
-                // shouldn't happen
-                Slog.wtf(LOG_TAG, "Failed to install " + packageName, re);
-            } finally {
-                restoreCallingIdentity(id);
-            }
-        }
-    }
-
-    @Override
-    public int enableSystemAppWithIntent(ComponentName who, Intent intent) {
-        synchronized (this) {
-            if (who == null) {
-                throw new NullPointerException("ComponentName is null");
-            }
-
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-
-            int userId = UserHandle.getCallingUserId();
-            long id = Binder.clearCallingIdentity();
-
-            try {
-                UserManager um = UserManager.get(mContext);
-                if (!um.getUserInfo(userId).isManagedProfile()) {
-                    throw new IllegalStateException(
-                            "Only call this method from a managed profile.");
-                }
-
-                // TODO: Use UserManager::getProfileParent when available.
-                UserInfo primaryUser = um.getUserInfo(UserHandle.USER_OWNER);
-
-                IPackageManager pm = AppGlobals.getPackageManager();
-                List<ResolveInfo> activitiesToEnable = pm.queryIntentActivities(intent,
-                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                        0, // no flags
-                        primaryUser.id);
-
-                if (DBG) Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
-                int numberOfAppsInstalled = 0;
-                if (activitiesToEnable != null) {
-                    for (ResolveInfo info : activitiesToEnable) {
-                        if (info.activityInfo != null) {
-
-                            if (!isSystemApp(pm, info.activityInfo.packageName, primaryUser.id)) {
-                                throw new IllegalArgumentException(
-                                        "Only system apps can be enabled this way.");
-                            }
-
-
-                            numberOfAppsInstalled++;
-                            pm.installExistingPackageAsUser(info.activityInfo.packageName, userId);
-                        }
-                    }
-                }
-                return numberOfAppsInstalled;
-            } catch (RemoteException e) {
-                // shouldn't happen
-                Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
-                return 0;
-            } finally {
-                restoreCallingIdentity(id);
-            }
-        }
-    }
-
-    private boolean isSystemApp(IPackageManager pm, String packageName, int userId)
-            throws RemoteException {
-        ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0, userId);
-        return (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0;
-    }
-
-    @Override
     public void setAccountManagementDisabled(ComponentName who, String accountType,
             boolean disabled) {
         if (!mHasFeature) {