Loading core/java/android/widget/TextView.java +53 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.widget; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY; Loading @@ -31,11 +32,13 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import android.annotation.RequiresPermission; import android.annotation.Size; import android.annotation.StringRes; import android.annotation.StyleRes; import android.annotation.XmlRes; import android.app.Activity; import android.app.ActivityManager; import android.app.PendingIntent; import android.app.assist.AssistStructure; import android.content.ClipData; Loading Loading @@ -72,6 +75,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.ParcelableParcel; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.text.BoringLayout; import android.text.DynamicLayout; Loading Loading @@ -723,6 +727,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private InputFilter[] mFilters = NO_FILTERS; /** * To keep the information to indicate if there is necessary to restrict the power of * INTERACT_ACROSS_USERS_FULL. * <p> * SystemUI always run as user 0 to process all of direct reply. SystemUI has the poer of * INTERACT_ACROSS_USERS_FULL. However, all of the notifications not only belong to user 0 but * also to the other users in multiple user environment. * </p> * * @see #setRestrictedAcrossUser(boolean) */ private boolean mIsRestrictedAcrossUser; private volatile Locale mCurrentSpellCheckerLocaleCache; // It is possible to have a selection even when mEditor is null (programmatically set, like when Loading Loading @@ -10439,6 +10456,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener : mCurrentSpellCheckerLocaleCache; } /** * To notify the TextView to restricted the power of the app granted INTERACT_ACROSS_USERS_FULL * permission. * <p> * Most of applications should not granted the INTERACT_ACROSS_USERS_FULL permssion. * SystemUI is the special one that run in user 0 process to handle multiple user notification. * Unforunately, the power of INTERACT_ACROSS_USERS_FULL should be limited or restricted for * preventing from information leak.</p> * <p>This function call is called for SystemUI Keyguard and Notification.</p> * * @param isRestricted is true if the power of INTERACT_ACROSS_USERS_FULL should be limited. * @hide */ @RequiresPermission(INTERACT_ACROSS_USERS_FULL) public final void setRestrictedAcrossUser(boolean isRestricted) { mIsRestrictedAcrossUser = isRestricted; } /** * This is a temporary method. Future versions may support multi-locale text. * Caveat: This method may not return the latest text services locale, but this should be Loading Loading @@ -11647,6 +11682,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean canCut() { if (mIsRestrictedAcrossUser && UserHandle.myUserId() != ActivityManager.getCurrentUser()) { // When it's restricted, and the curren user is not the process user. It can't cut // because it may cut the text of the user 10 into the clipboard of user 0. return false; } if (hasPasswordTransformationMethod()) { return false; } Loading @@ -11660,6 +11701,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean canCopy() { if (mIsRestrictedAcrossUser && UserHandle.myUserId() != ActivityManager.getCurrentUser()) { // When it's restricted, and the curren user is not the process user. It can't copy // because it may copy the text of the user 10 to the clipboard of user 0. return false; } if (hasPasswordTransformationMethod()) { return false; } Loading Loading @@ -11689,6 +11736,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean canPaste() { if (mIsRestrictedAcrossUser && UserHandle.myUserId() != ActivityManager.getCurrentUser()) { // When it's restricted, and the curren user is not the process user. It can't paste // because it may copy the text from the user 0 clipboard in current user is 10. return false; } return (mText instanceof Editable && mEditor != null && mEditor.mKeyListener != null && getSelectionStart() >= 0 Loading packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +2 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView @Override protected void resetState() { mPasswordEntry.setRestrictedAcrossUser(true); mSecurityMessageDisplay.setMessage(""); final boolean wasDisabled = mPasswordEntry.isEnabled(); setPasswordEntryEnabled(true); Loading Loading @@ -169,6 +170,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView Context.INPUT_METHOD_SERVICE); mPasswordEntry = findViewById(getPasswordTextViewId()); mPasswordEntry.setRestrictedAcrossUser(true); mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry); mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT Loading packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +39 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.policy; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; import android.app.RemoteInput; Loading @@ -28,11 +29,14 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.SystemClock; import android.os.UserHandle; import android.text.Editable; import android.text.InputType; import android.text.SpannedString; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.view.ActionMode; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; Loading @@ -45,6 +49,7 @@ import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.view.textclassifier.TextClassifier; import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; Loading Loading @@ -187,11 +192,35 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene LayoutInflater.from(context).inflate(R.layout.remote_input, root, false); v.mController = controller; v.mEntry = entry; v.mEditText.setRestrictedAcrossUser(true); v.setTag(VIEW_TAG); // Disable the TextClassifier to avoid cross user interactions. v.mEditText.setTextClassifier(TextClassifier.NO_OP); return v; } @Override public ActionMode startActionMode(ActionMode.Callback callback, int type) { try { UserHandle notificationUser = mEntry.notification.getUser(); UserHandle currentUser = UserHandle.of(ActivityManager.getCurrentUser()); if (!UserHandle.ALL.equals(notificationUser) && !currentUser.equals(notificationUser)) { // If this happens to be a selection action mode, a non-NO_OP TextClassifier could // leak data across users. This widget uses TextClassifier.NO_OP so this is fine. // Log the security fix. android.util.EventLog.writeEvent(0x534e4554, "123232892", -1, ""); } } catch (Throwable t) { // Avoid crashing because of this log attempt. Log.i(TAG, "Error attempting to log security fix for bug 123232892", t); } return super.startActionMode(callback, type); } @Override public void onClick(View v) { if (v == mSendButton) { Loading Loading @@ -292,6 +321,16 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (mWrapper != null) { mWrapper.setRemoteInputVisible(true); } // Disable suggestions on non-owner (secondary) user. // SpellCheckerService of primary user runs on secondary as well which shows // "Add to dictionary" dialog on the primary user. (See b/123232892) // Note: this doesn't affect work-profile users on P or older versions. if (UserHandle.myUserId() != ActivityManager.getCurrentUser()) { mEditText.setInputType( mEditText.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); } mEditText.setInnerFocusable(true); mEditText.mShowImeOnInputConnection = true; mEditText.setText(mEntry.remoteInputText); Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; import android.view.textclassifier.TextClassifier; import android.widget.EditText; import android.widget.ImageButton; Loading Loading @@ -109,4 +110,13 @@ public class RemoteInputViewTest extends SysuiTestCase { mView.setVisibility(View.INVISIBLE); mView.setVisibility(View.VISIBLE); } @Test public void testUsesNoOpTextClassifier() { RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).build(); mView.setRemoteInput(new RemoteInput[]{input}, input); EditText editText = mView.findViewById(R.id.remote_input_text); assertEquals(TextClassifier.NO_OP, editText.getTextClassifier()); } } services/core/java/com/android/server/ConnectivityService.java +4 −7 Original line number Diff line number Diff line Loading @@ -2356,9 +2356,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } } private boolean networkRequiresValidation(NetworkAgentInfo nai) { return NetworkMonitor.isValidationRequired( mDefaultRequest.networkCapabilities, nai.networkCapabilities); private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) { return nai.networkMonitor.isPrivateDnsValidationRequired(); } private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) { Loading @@ -2376,16 +2375,14 @@ public class ConnectivityService extends IConnectivityManager.Stub for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { handlePerNetworkPrivateDnsConfig(nai, cfg); if (networkRequiresValidation(nai)) { if (networkRequiresPrivateDnsValidation(nai)) { handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties)); } } } private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) { // Private DNS only ever applies to networks that might provide // Internet access and therefore also require validation. if (!networkRequiresValidation(nai)) return; if (!networkRequiresPrivateDnsValidation(nai)) return; // Notify the NetworkMonitor thread in case it needs to cancel or // schedule DNS resolutions. If a DNS resolution is required the Loading Loading
core/java/android/widget/TextView.java +53 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.widget; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY; Loading @@ -31,11 +32,13 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import android.annotation.RequiresPermission; import android.annotation.Size; import android.annotation.StringRes; import android.annotation.StyleRes; import android.annotation.XmlRes; import android.app.Activity; import android.app.ActivityManager; import android.app.PendingIntent; import android.app.assist.AssistStructure; import android.content.ClipData; Loading Loading @@ -72,6 +75,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.ParcelableParcel; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.text.BoringLayout; import android.text.DynamicLayout; Loading Loading @@ -723,6 +727,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private InputFilter[] mFilters = NO_FILTERS; /** * To keep the information to indicate if there is necessary to restrict the power of * INTERACT_ACROSS_USERS_FULL. * <p> * SystemUI always run as user 0 to process all of direct reply. SystemUI has the poer of * INTERACT_ACROSS_USERS_FULL. However, all of the notifications not only belong to user 0 but * also to the other users in multiple user environment. * </p> * * @see #setRestrictedAcrossUser(boolean) */ private boolean mIsRestrictedAcrossUser; private volatile Locale mCurrentSpellCheckerLocaleCache; // It is possible to have a selection even when mEditor is null (programmatically set, like when Loading Loading @@ -10439,6 +10456,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener : mCurrentSpellCheckerLocaleCache; } /** * To notify the TextView to restricted the power of the app granted INTERACT_ACROSS_USERS_FULL * permission. * <p> * Most of applications should not granted the INTERACT_ACROSS_USERS_FULL permssion. * SystemUI is the special one that run in user 0 process to handle multiple user notification. * Unforunately, the power of INTERACT_ACROSS_USERS_FULL should be limited or restricted for * preventing from information leak.</p> * <p>This function call is called for SystemUI Keyguard and Notification.</p> * * @param isRestricted is true if the power of INTERACT_ACROSS_USERS_FULL should be limited. * @hide */ @RequiresPermission(INTERACT_ACROSS_USERS_FULL) public final void setRestrictedAcrossUser(boolean isRestricted) { mIsRestrictedAcrossUser = isRestricted; } /** * This is a temporary method. Future versions may support multi-locale text. * Caveat: This method may not return the latest text services locale, but this should be Loading Loading @@ -11647,6 +11682,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean canCut() { if (mIsRestrictedAcrossUser && UserHandle.myUserId() != ActivityManager.getCurrentUser()) { // When it's restricted, and the curren user is not the process user. It can't cut // because it may cut the text of the user 10 into the clipboard of user 0. return false; } if (hasPasswordTransformationMethod()) { return false; } Loading @@ -11660,6 +11701,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean canCopy() { if (mIsRestrictedAcrossUser && UserHandle.myUserId() != ActivityManager.getCurrentUser()) { // When it's restricted, and the curren user is not the process user. It can't copy // because it may copy the text of the user 10 to the clipboard of user 0. return false; } if (hasPasswordTransformationMethod()) { return false; } Loading Loading @@ -11689,6 +11736,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean canPaste() { if (mIsRestrictedAcrossUser && UserHandle.myUserId() != ActivityManager.getCurrentUser()) { // When it's restricted, and the curren user is not the process user. It can't paste // because it may copy the text from the user 0 clipboard in current user is 10. return false; } return (mText instanceof Editable && mEditor != null && mEditor.mKeyListener != null && getSelectionStart() >= 0 Loading
packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +2 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView @Override protected void resetState() { mPasswordEntry.setRestrictedAcrossUser(true); mSecurityMessageDisplay.setMessage(""); final boolean wasDisabled = mPasswordEntry.isEnabled(); setPasswordEntryEnabled(true); Loading Loading @@ -169,6 +170,7 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView Context.INPUT_METHOD_SERVICE); mPasswordEntry = findViewById(getPasswordTextViewId()); mPasswordEntry.setRestrictedAcrossUser(true); mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry); mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT Loading
packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +39 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.policy; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; import android.app.RemoteInput; Loading @@ -28,11 +29,14 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.SystemClock; import android.os.UserHandle; import android.text.Editable; import android.text.InputType; import android.text.SpannedString; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.view.ActionMode; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; Loading @@ -45,6 +49,7 @@ import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.view.textclassifier.TextClassifier; import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; Loading Loading @@ -187,11 +192,35 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene LayoutInflater.from(context).inflate(R.layout.remote_input, root, false); v.mController = controller; v.mEntry = entry; v.mEditText.setRestrictedAcrossUser(true); v.setTag(VIEW_TAG); // Disable the TextClassifier to avoid cross user interactions. v.mEditText.setTextClassifier(TextClassifier.NO_OP); return v; } @Override public ActionMode startActionMode(ActionMode.Callback callback, int type) { try { UserHandle notificationUser = mEntry.notification.getUser(); UserHandle currentUser = UserHandle.of(ActivityManager.getCurrentUser()); if (!UserHandle.ALL.equals(notificationUser) && !currentUser.equals(notificationUser)) { // If this happens to be a selection action mode, a non-NO_OP TextClassifier could // leak data across users. This widget uses TextClassifier.NO_OP so this is fine. // Log the security fix. android.util.EventLog.writeEvent(0x534e4554, "123232892", -1, ""); } } catch (Throwable t) { // Avoid crashing because of this log attempt. Log.i(TAG, "Error attempting to log security fix for bug 123232892", t); } return super.startActionMode(callback, type); } @Override public void onClick(View v) { if (v == mSendButton) { Loading Loading @@ -292,6 +321,16 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (mWrapper != null) { mWrapper.setRemoteInputVisible(true); } // Disable suggestions on non-owner (secondary) user. // SpellCheckerService of primary user runs on secondary as well which shows // "Add to dictionary" dialog on the primary user. (See b/123232892) // Note: this doesn't affect work-profile users on P or older versions. if (UserHandle.myUserId() != ActivityManager.getCurrentUser()) { mEditText.setInputType( mEditText.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); } mEditText.setInnerFocusable(true); mEditText.mShowImeOnInputConnection = true; mEditText.setText(mEntry.remoteInputText); Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; import android.view.textclassifier.TextClassifier; import android.widget.EditText; import android.widget.ImageButton; Loading Loading @@ -109,4 +110,13 @@ public class RemoteInputViewTest extends SysuiTestCase { mView.setVisibility(View.INVISIBLE); mView.setVisibility(View.VISIBLE); } @Test public void testUsesNoOpTextClassifier() { RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).build(); mView.setRemoteInput(new RemoteInput[]{input}, input); EditText editText = mView.findViewById(R.id.remote_input_text); assertEquals(TextClassifier.NO_OP, editText.getTextClassifier()); } }
services/core/java/com/android/server/ConnectivityService.java +4 −7 Original line number Diff line number Diff line Loading @@ -2356,9 +2356,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } } private boolean networkRequiresValidation(NetworkAgentInfo nai) { return NetworkMonitor.isValidationRequired( mDefaultRequest.networkCapabilities, nai.networkCapabilities); private boolean networkRequiresPrivateDnsValidation(NetworkAgentInfo nai) { return nai.networkMonitor.isPrivateDnsValidationRequired(); } private void handleFreshlyValidatedNetwork(NetworkAgentInfo nai) { Loading @@ -2376,16 +2375,14 @@ public class ConnectivityService extends IConnectivityManager.Stub for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { handlePerNetworkPrivateDnsConfig(nai, cfg); if (networkRequiresValidation(nai)) { if (networkRequiresPrivateDnsValidation(nai)) { handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties)); } } } private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) { // Private DNS only ever applies to networks that might provide // Internet access and therefore also require validation. if (!networkRequiresValidation(nai)) return; if (!networkRequiresPrivateDnsValidation(nai)) return; // Notify the NetworkMonitor thread in case it needs to cancel or // schedule DNS resolutions. If a DNS resolution is required the Loading