Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ac6e1a36 authored by Aran Ink's avatar Aran Ink Committed by Android (Google) Code Review
Browse files

Merge "Allow insertion of images from IMEs into notification quick replies."

parents 8ac0fb07 fd2bfd34
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.internal.statusbar;

import android.app.Notification;
import android.net.Uri;
import android.content.ComponentName;
import android.graphics.Rect;
import android.os.Bundle;
@@ -77,6 +78,7 @@ interface IStatusBarService
    void onNotificationSettingsViewed(String key);
    void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
    void onNotificationBubbleChanged(String key, boolean isBubble);
    void grantInlineReplyUriPermission(String key, in Uri uri);

    void onGlobalActionsShown();
    void onGlobalActionsHidden();
+80 −8
Original line number Diff line number Diff line
@@ -23,12 +23,16 @@ import android.app.ActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.Editable;
@@ -53,8 +57,13 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

import androidx.core.view.inputmethod.EditorInfoCompat;
import androidx.core.view.inputmethod.InputConnectionCompat;
import androidx.core.view.inputmethod.InputContentInfoCompat;

import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -65,6 +74,7 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LightBarController;

import java.util.HashMap;
import java.util.function.Consumer;

/**
@@ -88,6 +98,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
    private RemoteInputController mController;
    private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;

    private IStatusBarService mStatusBarManagerService;

    private NotificationEntry mEntry;

    private boolean mRemoved;
@@ -103,6 +115,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
    public RemoteInputView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class);
        mStatusBarManagerService = IStatusBarService.Stub.asInterface(
                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
    }

    @Override
@@ -128,7 +142,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene

                if (isSoftImeEvent || isKeyboardEnterKey) {
                    if (mEditText.length() > 0) {
                        sendRemoteInput();
                        sendRemoteInput(prepareRemoteInputFromText());
                    }
                    // Consume action to prevent IME from closing.
                    return true;
@@ -141,7 +155,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
        mEditText.mRemoteInputView = this;
    }

    private void sendRemoteInput() {
    protected Intent prepareRemoteInputFromText() {
        Bundle results = new Bundle();
        results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
        Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -153,6 +167,25 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
            RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE);
        }

        return fillInIntent;
    }

    protected Intent prepareRemoteInputFromData(String contentType, Uri data) {
        HashMap<String, Uri> results = new HashMap<>();
        results.put(contentType, data);
        try {
            mStatusBarManagerService.grantInlineReplyUriPermission(
                    mEntry.notification.getKey(), data);
        } catch (Exception e) {
            Log.e(TAG, "Failed to grant URI permissions:" + e.getMessage(), e);
        }
        Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results);

        return fillInIntent;
    }

    private void sendRemoteInput(Intent intent) {
        mEditText.setEnabled(false);
        mSendButton.setVisibility(INVISIBLE);
        mProgressBar.setVisibility(VISIBLE);
@@ -176,7 +209,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
        MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
                mEntry.notification.getPackageName());
        try {
            mPendingIntent.send(mContext, 0, fillInIntent);
            mPendingIntent.send(mContext, 0, intent);
        } catch (PendingIntent.CanceledException e) {
            Log.i(TAG, "Unable to send remote input result", e);
            MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL,
@@ -195,7 +228,9 @@ 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.setTextOperationUser(computeTextOperationUser(entry.notification.getUser()));
        UserHandle user = computeTextOperationUser(entry.notification.getUser());
        v.mEditText.mUser = user;
        v.mEditText.setTextOperationUser(user);
        v.setTag(VIEW_TAG);

        return v;
@@ -204,7 +239,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
    @Override
    public void onClick(View v) {
        if (v == mSendButton) {
            sendRemoteInput();
            sendRemoteInput(prepareRemoteInputFromText());
        }
    }

@@ -518,6 +553,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
        private RemoteInputView mRemoteInputView;
        boolean mShowImeOnInputConnection;
        private LightBarController mLightBarController;
        UserHandle mUser;

        public RemoteEditText(Context context, AttributeSet attrs) {
            super(context, attrs);
@@ -617,11 +653,47 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene

        @Override
        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
            String[] allowedDataTypes = mRemoteInputView.mRemoteInput.getAllowedDataTypes()
                    .toArray(new String[0]);
            EditorInfoCompat.setContentMimeTypes(outAttrs, allowedDataTypes);
            final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);

            if (mShowImeOnInputConnection && inputConnection != null) {
            final InputConnectionCompat.OnCommitContentListener callback =
                    new InputConnectionCompat.OnCommitContentListener() {
                        @Override
                        public boolean onCommitContent(
                                InputContentInfoCompat inputContentInfoCompat, int i,
                                Bundle bundle) {
                            Uri contentUri = inputContentInfoCompat.getContentUri();
                            ClipDescription description = inputContentInfoCompat.getDescription();
                            String mimeType = null;
                            if (description != null && description.getMimeTypeCount() > 0) {
                                mimeType = description.getMimeType(0);
                            }
                            if (mimeType != null) {
                                Intent dataIntent = mRemoteInputView.prepareRemoteInputFromData(
                                        mimeType, contentUri);
                                mRemoteInputView.sendRemoteInput(dataIntent);
                            }
                            return true;
                        }
                    };

            InputConnection ic = InputConnectionCompat.createWrapper(
                    inputConnection, outAttrs, callback);

            Context userContext = null;
            try {
                userContext = mContext.createPackageContextAsUser(
                        mContext.getPackageName(), 0, mUser);
            } catch (PackageManager.NameNotFoundException e) {
                Log.e(TAG, "Unable to create user context:" + e.getMessage(), e);
            }

            if (mShowImeOnInputConnection && ic != null) {
                Context targetContext = userContext != null ? userContext : getContext();
                final InputMethodManager imm =
                        getContext().getSystemService(InputMethodManager.class);
                        targetContext.getSystemService(InputMethodManager.class);
                if (imm != null) {
                    // onCreateInputConnection is called by InputMethodManager in the middle of
                    // setting up the connection to the IME; wait with requesting the IME until that
@@ -636,7 +708,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene
                }
            }

            return inputConnection;
            return ic;
        }

        @Override
+7 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.notification;

import android.app.Notification;
import android.net.Uri;
import android.service.notification.NotificationStats;

import com.android.internal.statusbar.NotificationVisibility;
@@ -48,6 +49,12 @@ public interface NotificationDelegate {
    void onNotificationSettingsViewed(String key);
    void onNotificationBubbleChanged(String key, boolean isBubble);

    /**
     * Grant permission to read the specified URI to the package associated with the
     * NotificationRecord associated with the given key.
     */
    void grantInlineReplyUriPermission(String key, Uri uri, int callingUid);

    /**
     * Notifies that smart replies and actions have been added to the UI.
     */
+50 −1
Original line number Diff line number Diff line
@@ -1154,6 +1154,56 @@ public class NotificationManagerService extends SystemService {
                }
            }
        }

        @Override
        /**
         * Grant permission to read the specified URI to the package specified in the
         * NotificationRecord associated with the given key. The callingUid represents the UID of
         * SystemUI from which this method is being called.
         *
         * For this to work, SystemUI must have permission to read the URI when running under the
         * user associated with the NotificationRecord, and this grant will fail when trying
         * to grant URI permissions across users.
         */
        public void grantInlineReplyUriPermission(String key, Uri uri, int callingUid) {
            synchronized (mNotificationLock) {
                NotificationRecord r = mNotificationsByKey.get(key);
                if (r != null) {
                    IBinder owner = r.permissionOwner;
                    if (owner == null) {
                        r.permissionOwner = mUgmInternal.newUriPermissionOwner("NOTIF:" + key);
                        owner = r.permissionOwner;
                    }
                    int uid = callingUid;
                    int userId = r.sbn.getUserId();
                    if (userId == UserHandle.USER_ALL) {
                        userId = USER_SYSTEM;
                    }
                    if (UserHandle.getUserId(uid) != userId) {
                        try {
                            final String[] pkgs = mPackageManager.getPackagesForUid(callingUid);
                            if (pkgs == null) {
                                Log.e(TAG, "Cannot grant uri permission to unknown UID: "
                                        + callingUid);
                            }
                            final String pkg = pkgs[0]; // Get the SystemUI package
                            // Find the UID for SystemUI for the correct user
                            uid =  mPackageManager.getPackageUid(pkg, 0, userId);
                        } catch (RemoteException re) {
                            Log.e(TAG, "Cannot talk to package manager", re);
                        }
                    }
                    grantUriPermission(owner, uri, uid, r.sbn.getPackageName(), userId);
                } else {
                    Log.w(TAG, "No record found for notification key:" + key);

                    // TODO: figure out cancel story. I think it's: sysui needs to tell us
                    // whenever noitifications held by a lifetimextender go away
                    // IBinder owner = mUgmInternal.newUriPermissionOwner("InlineReply:" + key);
                    // pass in userId and package as well as key (key for logging purposes)
                }
            }
        }
    };

    @VisibleForTesting
@@ -7012,7 +7062,6 @@ public class NotificationManagerService extends SystemService {
    private void grantUriPermission(IBinder owner, Uri uri, int sourceUid, String targetPkg,
            int targetUserId) {
        if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;

        final long ident = Binder.clearCallingIdentity();
        try {
            mUgm.grantUriPermissionFromOwner(owner, sourceUid, targetPkg,
+13 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.graphics.Rect;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -1333,6 +1334,18 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
        }
    }

    @Override
    public void grantInlineReplyUriPermission(String key, Uri uri) {
        enforceStatusBarService();
        int callingUid = Binder.getCallingUid();
        long identity = Binder.clearCallingIdentity();
        try {
            mNotificationDelegate.grantInlineReplyUriPermission(key, uri, callingUid);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    @Override
    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Loading