Add Device Policy API to disable screen capture.

WindowManager will set secure flag on SurfaceControl for
all windows of a flagged user to prevent screen capture.
API is consistent with the camera disable API.

Change-Id: Ib180f67f1ad827b6f4aca2af615274256cce58f4
diff --git a/api/current.txt b/api/current.txt
index 3b4edcd..eb8cc87 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5355,6 +5355,7 @@
     method public int getPasswordMinimumSymbols(android.content.ComponentName);
     method public int getPasswordMinimumUpperCase(android.content.ComponentName);
     method public int getPasswordQuality(android.content.ComponentName);
+    method public boolean getScreenCaptureDisabled(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
     method public boolean hasAnyCaCertsInstalled();
@@ -5399,6 +5400,7 @@
     method public void setProfileName(android.content.ComponentName, java.lang.String);
     method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
     method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
+    method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index efeded5..a193a34 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1775,6 +1775,46 @@
     }
 
     /**
+     * Called by a device/profile owner to set whether the screen capture is disabled.
+     *
+     * <p>The calling device admin must be a device or profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     */
+    public void setScreenCaptureDisabled(ComponentName admin, boolean disabled) {
+        if (mService != null) {
+            try {
+                mService.setScreenCaptureDisabled(admin, UserHandle.myUserId(), disabled);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Determine whether or not screen capture has been disabled by the current
+     * admin, if specified, or all admins.
+     * @param admin The name of the admin component to check, or null to check if any admins
+     * have disabled screen capture.
+     */
+    public boolean getScreenCaptureDisabled(ComponentName admin) {
+        return getScreenCaptureDisabled(admin, UserHandle.myUserId());
+    }
+
+    /** @hide per-user version */
+    public boolean getScreenCaptureDisabled(ComponentName admin, int userHandle) {
+        if (mService != null) {
+            try {
+                return mService.getScreenCaptureDisabled(admin, userHandle);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Called by an application that is administering the device to disable keyguard customizations,
      * such as widgets. After setting this, keyguard features will be disabled according to the
      * provided feature list.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5fc8c5f..6499ae4 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -88,6 +88,9 @@
     void setCameraDisabled(in ComponentName who, boolean disabled, int userHandle);
     boolean getCameraDisabled(in ComponentName who, int userHandle);
 
+    void setScreenCaptureDisabled(in ComponentName who, int userHandle, boolean disabled);
+    boolean getScreenCaptureDisabled(in ComponentName who, int userHandle);
+
     void setKeyguardDisabledFeatures(in ComponentName who, int which, int userHandle);
     int getKeyguardDisabledFeatures(in ComponentName who, int userHandle);
 
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index a61d771..5157c41 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -149,6 +149,12 @@
     // boolean string as parsed by SystemProperties.getBoolean().
     void setStrictModeVisualIndicatorPreference(String enabled);
 
+    /**
+     * Update the windowmanagers cached value of
+     * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled(null, userId)}
+     */
+    void updateScreenCaptureDisabled(int userId);
+
     // These can only be called with the SET_ORIENTATION permission.
     /**
      * Update the current screen rotation based on the current state of
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cf2f5d3..416cd54 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -191,7 +191,7 @@
     <!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
     <string name="screenshot_failed_title">Couldn\'t capture screenshot.</string>
     <!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
-    <string name="screenshot_failed_text">Couldn\'t save screenshot. Storage may be in use.</string>
+    <string name="screenshot_failed_text">Can\'t take screenshot due to limited storage space, or it isn\'t allowed by the app or your organization.</string>
 
     <!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] -->
     <string name="usb_preference_title">USB file transfer options</string>
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index aae2bb8..eac2819 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -19,6 +19,7 @@
 import static android.view.WindowManager.LayoutParams.*;
 
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import android.app.admin.DevicePolicyManager;
 import android.app.AppOpsManager;
 import android.util.ArraySet;
 import android.util.TimeUtils;
@@ -425,6 +426,13 @@
      */
     WindowState[] mRebuildTmp = new WindowState[20];
 
+    /**
+     * Stores for each user whether screencapture is disabled
+     * This array is essentially a cache for all userId for
+     * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled(null, userId)}
+     */
+    SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<Boolean>();
+
     IInputMethodManager mInputMethodManager;
 
     AccessibilityController mAccessibilityController;
@@ -2439,6 +2447,45 @@
         return res;
     }
 
+    /**
+     * Returns whether screen capture is disabled for all windows of a specific user.
+     */
+    boolean isScreenCaptureDisabledLocked(int userId) {
+        Boolean disabled = mScreenCaptureDisabled.get(userId);
+        if (disabled != null) {
+            return disabled;
+        }
+
+        // mScreenCaptureDisabled not set yet, try to update it.
+        updateScreenCaptureDisabledLocked(userId);
+        disabled = mScreenCaptureDisabled.get(userId);
+        if (disabled == null) {
+            // Not able to update, return false by default.
+            return false;
+        } else {
+            return disabled;
+        }
+    }
+
+    /**
+     * Update mScreenCaptureDisabled for specific user according to the device policy manager.
+     */
+    @Override
+    public void updateScreenCaptureDisabled(int userId) {
+        mH.sendMessage(mH.obtainMessage(H.UPDATE_SCRN_CAP, userId, 0 /* unused argument */));
+    }
+
+    void updateScreenCaptureDisabledLocked(int userId) {
+        DevicePolicyManager dpm = (DevicePolicyManager) mContext
+                .getSystemService(Context.DEVICE_POLICY_SERVICE);
+        if (dpm != null) {
+            boolean disabled = dpm.getScreenCaptureDisabled(null, userId);
+            mScreenCaptureDisabled.put(userId, disabled);
+        } else {
+            Slog.e(TAG, "Could not get DevicePolicyManager.");
+        }
+    }
+
     public void removeWindow(Session session, IWindow client) {
         synchronized(mWindowMap) {
             WindowState win = windowForClientLocked(session, client, false);
@@ -7203,6 +7250,8 @@
 
         public static final int NEW_ANIMATOR_SCALE = 34;
 
+        public static final int UPDATE_SCRN_CAP = 35;
+
         @Override
         public void handleMessage(Message msg) {
             if (DEBUG_WINDOW_TRACE) {
@@ -7677,6 +7726,13 @@
                     }
                 }
                 break;
+
+                case UPDATE_SCRN_CAP: {
+                    synchronized (mWindowMap) {
+                        updateScreenCaptureDisabledLocked(msg.arg1);
+                    }
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG, "handleMessage: exit");
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 49d4ae9..f3afe82 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -39,6 +39,7 @@
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.Debug;
+import android.os.UserHandle;
 import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -690,6 +691,10 @@
                 flags |= SurfaceControl.SECURE;
             }
 
+            if (mService.isScreenCaptureDisabledLocked(UserHandle.getUserId(mWin.mOwnerUid))) {
+                flags |= SurfaceControl.SECURE;
+            }
+
             int width;
             int height;
             if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5c661af..e0612eb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -258,6 +258,7 @@
         private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
         private static final String TAG_DISABLE_CAMERA = "disable-camera";
         private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
+        private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
         private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
         private static final String TAG_ACCOUNT_TYPE = "account-type";
         private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
@@ -326,6 +327,8 @@
         boolean encryptionRequested = false;
         boolean disableCamera = false;
         boolean disableCallerId = false;
+        boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
+
         Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
 
         // TODO: review implementation decisions with frameworks team
@@ -443,6 +446,11 @@
                 out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId));
                 out.endTag(null, TAG_DISABLE_CALLER_ID);
             }
+            if (disableScreenCapture) {
+                out.startTag(null, TAG_DISABLE_SCREEN_CAPTURE);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(disableScreenCapture));
+                out.endTag(null, TAG_DISABLE_SCREEN_CAPTURE);
+            }
             if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
                 out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
                 out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
@@ -528,6 +536,9 @@
                 } else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
                     disableCallerId = Boolean.parseBoolean(
                             parser.getAttributeValue(null, ATTR_VALUE));
+                } else if (TAG_DISABLE_SCREEN_CAPTURE.equals(tag)) {
+                    disableScreenCapture = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_VALUE));
                 } else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
                     disabledKeyguardFeatures = Integer.parseInt(
                             parser.getAttributeValue(null, ATTR_VALUE));
@@ -606,6 +617,8 @@
                     pw.println(disableCamera);
             pw.print(prefix); pw.print("disableCallerId=");
                     pw.println(disableCallerId);
+            pw.print(prefix); pw.print("disableScreenCapture=");
+                    pw.println(disableScreenCapture);
             pw.print(prefix); pw.print("disabledKeyguardFeatures=");
                     pw.println(disabledKeyguardFeatures);
         }
@@ -2977,6 +2990,58 @@
     private void setEncryptionRequested(boolean encrypt) {
     }
 
+
+    /**
+     * Set whether the screen capture is disabled for the user managed by the specified admin.
+     */
+    public void setScreenCaptureDisabled(ComponentName who, int userHandle, boolean disabled) {
+        if (!mHasFeature) {
+            return;
+        }
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (ap.disableScreenCapture != disabled) {
+                ap.disableScreenCapture = disabled;
+                saveSettingsLocked(userHandle);
+                try {
+                    getWindowManager().updateScreenCaptureDisabled(userHandle);
+                } catch (RemoteException e) {
+                    Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns whether or not screen capture is disabled for a given admin, or disabled for any
+     * active admin (if given admin is null).
+     */
+    public boolean getScreenCaptureDisabled(ComponentName who, int userHandle) {
+        if (!mHasFeature) {
+            return false;
+        }
+        synchronized (this) {
+            if (who != null) {
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+                return (admin != null) ? admin.disableScreenCapture : false;
+            }
+
+            DevicePolicyData policy = getUserData(userHandle);
+            final int N = policy.mAdminList.size();
+            for (int i = 0; i < N; i++) {
+                ActiveAdmin admin = policy.mAdminList.get(i);
+                if (admin.disableScreenCapture) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
     /**
      * The system property used to share the state of the camera. The native camera service
      * is expected to read this property and act accordingly.
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 6927b26..2ffe4a2 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -360,6 +360,11 @@
     }
 
     @Override
+    public void updateScreenCaptureDisabled(int userId) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
     public void updateRotation(boolean arg0, boolean arg1) throws RemoteException {
         // TODO Auto-generated method stub
     }