Implement issue #22403908: Enable assistant to refuse context sharing

New APIs allow the voice interaction service to set/retrieve a filter
for which of the show flags are allowed.

Change-Id: I588cbe55afee0548ad3afa22d3a7d3bc43cb54a6
diff --git a/api/current.txt b/api/current.txt
index df9ea35..ea33eaf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28785,11 +28785,13 @@
   public class VoiceInteractionService extends android.app.Service {
     ctor public VoiceInteractionService();
     method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(java.lang.String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
+    method public int getDisabledShowContext();
     method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onLaunchVoiceAssistFromKeyguard();
     method public void onReady();
     method public void onShutdown();
+    method public void setDisabledShowContext(int);
     method public void showSession(android.os.Bundle, int);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
     field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
@@ -28801,6 +28803,7 @@
     method public void closeSystemDialogs();
     method public void finish();
     method public android.content.Context getContext();
+    method public int getDisabledShowContext();
     method public android.view.LayoutInflater getLayoutInflater();
     method public android.app.Dialog getWindow();
     method public void hide();
@@ -28832,6 +28835,7 @@
     method public void onTaskStarted(android.content.Intent, int);
     method public void onTrimMemory(int);
     method public void setContentView(android.view.View);
+    method public void setDisabledShowContext(int);
     method public void setKeepAwake(boolean);
     method public void setTheme(int);
     method public void show(android.os.Bundle, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index eb1c431..cbb1bb2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -30934,11 +30934,13 @@
   public class VoiceInteractionService extends android.app.Service {
     ctor public VoiceInteractionService();
     method public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(java.lang.String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
+    method public int getDisabledShowContext();
     method public static boolean isActiveService(android.content.Context, android.content.ComponentName);
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onLaunchVoiceAssistFromKeyguard();
     method public void onReady();
     method public void onShutdown();
+    method public void setDisabledShowContext(int);
     method public void showSession(android.os.Bundle, int);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
     field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
@@ -30950,6 +30952,7 @@
     method public void closeSystemDialogs();
     method public void finish();
     method public android.content.Context getContext();
+    method public int getDisabledShowContext();
     method public android.view.LayoutInflater getLayoutInflater();
     method public android.app.Dialog getWindow();
     method public void hide();
@@ -30981,6 +30984,7 @@
     method public void onTaskStarted(android.content.Intent, int);
     method public void onTrimMemory(int);
     method public void setContentView(android.view.View);
+    method public void setDisabledShowContext(int);
     method public void setKeepAwake(boolean);
     method public void setTheme(int);
     method public void show(android.os.Bundle, int);
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 549c93e..479c9e2 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -37,7 +37,6 @@
 import java.io.PrintWriter;
 import java.util.Locale;
 
-
 /**
  * Top-level service of the current global voice interactor, which is providing
  * support for hotwording, the back-end of a {@link android.app.VoiceInteractor}, etc.
@@ -154,11 +153,39 @@
     }
 
     /**
+     * Set contextual options you would always like to have disabled when a session
+     * is shown.  The flags may be any combination of
+     * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
+     * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
+     * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}.
+     */
+    public void setDisabledShowContext(int flags) {
+        try {
+            mSystemService.setDisabledShowContext(flags);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Return the value set by {@link #setDisabledShowContext}.
+     */
+    public int getDisabledShowContext() {
+        try {
+            return mSystemService.getDisabledShowContext();
+        } catch (RemoteException e) {
+            return 0;
+        }
+    }
+
+    /**
      * Request that the associated {@link android.service.voice.VoiceInteractionSession} be
      * shown to the user, starting it if necessary.
      * @param args Arbitrary arguments that will be propagated to the session.
      * @param flags Indicates additional optional behavior that should be performed.  May
-     * be {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST}
+     * be any combination of
+     * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
+     * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
+      * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
      * to request that the system generate and deliver assist data on the current foreground
      * app as part of showing the session UI.
      */
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index e408b36..a2eb353 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -908,12 +908,38 @@
     }
 
     /**
+     * Equivalent to {@link VoiceInteractionService#setDisabledShowContext
+     * VoiceInteractionService.setDisabledShowContext(int)}.
+     */
+    public void setDisabledShowContext(int flags) {
+        try {
+            mSystemService.setDisabledShowContext(flags);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Equivalent to {@link VoiceInteractionService#getDisabledShowContext
+     * VoiceInteractionService.getDisabledShowContext}.
+     */
+    public int getDisabledShowContext() {
+        try {
+            return mSystemService.getDisabledShowContext();
+        } catch (RemoteException e) {
+            return 0;
+        }
+    }
+
+    /**
      * Show the UI for this session.  This asks the system to go through the process of showing
      * your UI, which will eventually culminate in {@link #onShow}.  This is similar to calling
      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
      * @param args Arbitrary arguments that will be propagated {@link #onShow}.
      * @param flags Indicates additional optional behavior that should be performed.  May
-     * be {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST}
+     * be any combination of
+     * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
+     * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
+     * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
      * to request that the system generate and deliver assist data on the current foreground
      * app as part of showing the session UI.
      */
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 7f54f50..73ad981 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -37,6 +37,8 @@
     void setKeepAwake(IBinder token, boolean keepAwake);
     void closeSystemDialogs(IBinder token);
     void finish(IBinder token);
+    void setDisabledShowContext(int flags);
+    int getDisabledShowContext();
 
     /**
      * Gets the registered Sound model for keyphrase detection for the current user.
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 36478da..42f879c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -578,6 +578,44 @@
             }
         }
 
+        @Override
+        public void setDisabledShowContext(int flags) {
+            synchronized (this) {
+                if (mImpl == null) {
+                    Slog.w(TAG, "setDisabledShowContext without running voice interaction service");
+                    return;
+                }
+                final int callingPid = Binder.getCallingPid();
+                final int callingUid = Binder.getCallingUid();
+                final long caller = Binder.clearCallingIdentity();
+                try {
+                    mImpl.setDisabledShowContextLocked(callingPid, callingUid, flags);
+                } finally {
+                    Binder.restoreCallingIdentity(caller);
+                }
+            }
+
+        }
+
+        @Override
+        public int getDisabledShowContext() {
+            synchronized (this) {
+                if (mImpl == null) {
+                    Slog.w(TAG, "getDisabledShowContext without running voice interaction service");
+                    return 0;
+                }
+                final int callingPid = Binder.getCallingPid();
+                final int callingUid = Binder.getCallingUid();
+                final long caller = Binder.clearCallingIdentity();
+                try {
+                    return mImpl.getDisabledShowContextLocked(callingPid, callingUid);
+                } finally {
+                    Binder.restoreCallingIdentity(caller);
+                }
+            }
+
+        }
+
         //----------------- Model management APIs --------------------------------//
 
         @Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index e5faf4d..7409f99 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -65,6 +65,7 @@
     IVoiceInteractionService mService;
 
     VoiceInteractionSessionConnection mActiveSession;
+    int mDisabledShowContext;
 
     final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -146,7 +147,7 @@
             mActiveSession = new VoiceInteractionSessionConnection(mLock, mSessionComponentName,
                     mUser, mContext, this, mInfo.getServiceInfo().applicationInfo.uid, mHandler);
         }
-        return mActiveSession.showLocked(args, flags, showCallback);
+        return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback);
     }
 
     public boolean hideSessionLocked() {
@@ -222,6 +223,24 @@
         mActiveSession = null;
     }
 
+    public void setDisabledShowContextLocked(int callingPid, int callingUid, int flags) {
+        int activeUid = mInfo.getServiceInfo().applicationInfo.uid;
+        if (callingUid != activeUid) {
+            throw new SecurityException("Calling uid " + callingUid
+                    + " does not match active uid " + activeUid);
+        }
+        mDisabledShowContext = flags;
+    }
+
+    public int getDisabledShowContextLocked(int callingPid, int callingUid) {
+        int activeUid = mInfo.getServiceInfo().applicationInfo.uid;
+        if (callingUid != activeUid) {
+            throw new SecurityException("Calling uid " + callingUid
+                    + " does not match active uid " + activeUid);
+        }
+        return mDisabledShowContext;
+    }
+
     public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!mValid) {
             pw.print("  NOT VALID: ");
@@ -235,6 +254,10 @@
         pw.print("  mComponent="); pw.println(mComponent.flattenToShortString());
         pw.print("  Session service="); pw.println(mInfo.getSessionService());
         pw.print("  Settings activity="); pw.println(mInfo.getSettingsActivity());
+        if (mDisabledShowContext != 0) {
+            pw.print("  mDisabledShowContext=");
+            pw.println(Integer.toHexString(mDisabledShowContext));
+        }
         pw.print("  mBound="); pw.print(mBound);  pw.print(" mService="); pw.println(mService);
         if (mActiveSession != null) {
             pw.println("  Active session:");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index bd043ac..dfdd639 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -183,7 +183,7 @@
         }
     }
 
-    public boolean showLocked(Bundle args, int flags,
+    public boolean showLocked(Bundle args, int flags, int disabledContext,
             IVoiceInteractionSessionShowCallback showCallback) {
         if (mBound) {
             if (!mFullyBound) {
@@ -200,15 +200,17 @@
             }
             boolean structureEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                     Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, mUser) != 0
-                    && isScreenCaptureAllowed;
+                    && isScreenCaptureAllowed
+                    && (disabledContext&VoiceInteractionSession.SHOW_WITH_ASSIST) == 0;
             boolean screenshotEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                     Settings.Secure.ASSIST_SCREENSHOT_ENABLED, 1, mUser) != 0
-                    && isScreenCaptureAllowed;
+                    && isScreenCaptureAllowed
+                    && (disabledContext&VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0;
             mShowArgs = args;
             mShowFlags = flags;
             mHaveAssistData = false;
             boolean needDisclosure = false;
-            if ((flags& VoiceInteractionSession.SHOW_WITH_ASSIST) != 0) {
+            if ((flags&VoiceInteractionSession.SHOW_WITH_ASSIST) != 0) {
                 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_STRUCTURE, mCallingUid,
                         mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED
                         && structureEnabled) {
@@ -226,7 +228,7 @@
                 mAssistData = null;
             }
             mHaveScreenshot = false;
-            if ((flags& VoiceInteractionSession.SHOW_WITH_SCREENSHOT) != 0) {
+            if ((flags&VoiceInteractionSession.SHOW_WITH_SCREENSHOT) != 0) {
                 if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ASSIST_SCREENSHOT, mCallingUid,
                         mSessionComponentName.getPackageName()) == AppOpsManager.MODE_ALLOWED
                         && screenshotEnabled) {
diff --git a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
index 610f30b..dc4e31b 100644
--- a/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
+++ b/tests/VoiceInteraction/res/layout/voice_interaction_session.xml
@@ -31,33 +31,56 @@
         android:layout_height="match_parent"
         android:fitsSystemWindows="true">
 
-        <LinearLayout android:id="@+id/top_content"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="top"
-            android:orientation="horizontal"
-            android:background="#ffffffff"
-            android:elevation="8dp"
-            >
-            <ImageView android:id="@+id/screenshot"
-                android:layout_width="wrap_content"
-                android:layout_height="46dp"
-                android:adjustViewBounds="true" />
-            <View android:layout_width="0dp"
-                android:layout_height="0dp"
-                android:layout_weight="1" />
-            <Button android:id="@+id/do_tree"
-                android:layout_width="wrap_content"
+        <LinearLayout
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/tree" />
-            <Button android:id="@+id/do_text"
-                android:layout_width="wrap_content"
+                android:layout_gravity="top"
+                android:orientation="vertical"
+                android:background="#ffffffff"
+                android:elevation="8dp"
+                >
+            <LinearLayout android:id="@+id/top_content"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/text" />
-            <Button android:id="@+id/start"
-                android:layout_width="wrap_content"
+                android:orientation="horizontal"
+                >
+                <ImageView android:id="@+id/screenshot"
+                    android:layout_width="wrap_content"
+                    android:layout_height="46dp"
+                    android:adjustViewBounds="true" />
+                <View android:layout_width="0dp"
+                    android:layout_height="0dp"
+                    android:layout_weight="1" />
+                <CheckBox android:id="@+id/show_options"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content" />
+                <Button android:id="@+id/do_tree"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/tree" />
+                <Button android:id="@+id/do_text"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/text" />
+                <Button android:id="@+id/start"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/start" />
+            </LinearLayout>
+            <LinearLayout android:id="@+id/options"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:text="@string/start" />
+                android:orientation="vertical"
+                >
+                <CheckBox android:id="@+id/disallow_structure"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="Disallow context" />
+                <CheckBox android:id="@+id/disallow_screenshot"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="Disallow screenshot" />
+            </LinearLayout>
         </LinearLayout>
 
         <LinearLayout android:id="@+id/bottom_content"
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index a6585ba..8796c9f 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
+import android.widget.CheckBox;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -45,6 +46,10 @@
     Button mTreeButton;
     Button mTextButton;
     Button mStartButton;
+    CheckBox mOptionsCheck;
+    View mOptionsContainer;
+    CheckBox mDisallowAssist;
+    CheckBox mDisallowScreenshot;
     ImageView mScreenshot;
     ImageView mFullScreenshot;
     Button mConfirmButton;
@@ -122,15 +127,34 @@
         mScreenshot = (ImageView)mContentView.findViewById(R.id.screenshot);
         mScreenshot.setOnClickListener(this);
         mFullScreenshot = (ImageView)mContentView.findViewById(R.id.full_screenshot);
+        mOptionsCheck = (CheckBox)mContentView.findViewById(R.id.show_options);
+        mOptionsCheck.setOnClickListener(this);
+        mOptionsContainer = mContentView.findViewById(R.id.options);
+        mDisallowAssist = (CheckBox)mContentView.findViewById(R.id.disallow_structure);
+        mDisallowAssist.setOnClickListener(this);
+        mDisallowScreenshot = (CheckBox)mContentView.findViewById(R.id.disallow_screenshot);
+        mDisallowScreenshot.setOnClickListener(this);
         mConfirmButton = (Button)mContentView.findViewById(R.id.confirm);
         mConfirmButton.setOnClickListener(this);
         mCompleteButton = (Button)mContentView.findViewById(R.id.complete);
         mCompleteButton.setOnClickListener(this);
         mAbortButton = (Button)mContentView.findViewById(R.id.abort);
         mAbortButton.setOnClickListener(this);
+        refreshOptions();
         return mContentView;
     }
 
+    void refreshOptions() {
+        if (mOptionsCheck.isChecked()) {
+            mOptionsContainer.setVisibility(View.VISIBLE);
+            int flags = getDisabledShowContext();
+            mDisallowAssist.setChecked((flags & SHOW_WITH_ASSIST) != 0);
+            mDisallowScreenshot.setChecked((flags & SHOW_WITH_SCREENSHOT) != 0);
+        } else {
+            mOptionsContainer.setVisibility(View.GONE);
+        }
+    }
+
     public void onHandleAssist(Bundle assistBundle) {
     }
 
@@ -202,6 +226,24 @@
             if (mAssistVisualizer != null) {
                 mAssistVisualizer.logText();
             }
+        } else if (v == mOptionsCheck) {
+            refreshOptions();
+        } else if (v == mDisallowAssist) {
+            int flags = getDisabledShowContext();
+            if (mDisallowAssist.isChecked()) {
+                flags |= SHOW_WITH_ASSIST;
+            } else {
+                flags &= ~SHOW_WITH_ASSIST;
+            }
+            setDisabledShowContext(flags);
+        } else if (v == mDisallowScreenshot) {
+            int flags = getDisabledShowContext();
+            if (mDisallowScreenshot.isChecked()) {
+                flags |= SHOW_WITH_SCREENSHOT;
+            } else {
+                flags &= ~SHOW_WITH_SCREENSHOT;
+            }
+            setDisabledShowContext(flags);
         } else if (v == mStartButton) {
             mState = STATE_LAUNCHING;
             updateState();