diff options
| -rw-r--r-- | core/res/AndroidManifest.xml | 4 | ||||
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/AutoFillManagerService.java | 154 |
2 files changed, 155 insertions, 3 deletions
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 0f7b5a53e33c..765a326a7f25 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -501,6 +501,10 @@ <protected-broadcast android:name="android.intent.action.DEVICE_LOCKED_CHANGED" /> + <!-- Added in O --> + <!-- TODO: temporary broadcast used by AutoFillManagerServiceImpl; will be removed --> + <protected-broadcast android:name="com.android.internal.autofill.action.REQUEST_AUTOFILL" /> + <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> <!-- ====================================================================== --> diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java index 9157e83a3dd1..d70c4391e274 100644 --- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java @@ -20,26 +20,32 @@ import static android.Manifest.permission.MANAGE_AUTO_FILL; import static android.content.Context.AUTO_FILL_MANAGER_SERVICE; import android.Manifest; -import android.app.ActivityManager; import android.app.AppGlobals; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; -import android.os.Parcel; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.service.autofill.IAutoFillManagerService; import android.text.TextUtils; @@ -56,6 +62,7 @@ import com.android.server.SystemService; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.List; /** * Entry point service for auto-fill management. @@ -88,6 +95,9 @@ public final class AutoFillManagerService extends SystemService { case MSG_UNBIND: removeStaleServiceForUser(msg.arg1); return; + case MSG_SHOW_ALL_NOTIFICATIONS: + showAllNotifications(); + return; default: Slog.w(TAG, "Invalid message: " + msg); } @@ -131,6 +141,14 @@ public final class AutoFillManagerService extends SystemService { if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { new SettingsObserver(BackgroundThread.getHandler()); } + if (phase == PHASE_BOOT_COMPLETED) { + // TODO: if sent right away, the notification is not displayed. Since the notification + // mechanism is a temporary approach anyways, just delay it.. + if (DEBUG) + Slog.d(TAG, "Showing notifications in " + SHOW_ALL_NOTIFICATIONS_DELAY_MS + "ms"); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ALL_NOTIFICATIONS), + SHOW_ALL_NOTIFICATIONS_DELAY_MS); + } } private AutoFillManagerServiceImpl newServiceForUser(int userId) { @@ -224,6 +242,7 @@ public final class AutoFillManagerService extends SystemService { } mServicesCache.delete(userId); service.stopLocked(); + } final class AutoFillManagerServiceStub extends IAutoFillManagerService.Stub { @@ -287,7 +306,136 @@ public final class AutoFillManagerService extends SystemService { if (DEBUG) Slog.d(TAG, "settings (" + uri + " changed for " + userId); synchronized (mLock) { removeCachedServiceForUserLocked(userId); + final ComponentName serviceComponent = getProviderForUser(userId); + if (serviceComponent== null) { + cancelNotificationLocked(userId); + } else { + showNotification(serviceComponent, userId); + } + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // TODO: temporary code using a notification to request auto-fill. // + // Will be removed once UX decide the right way to present it to the user // + //////////////////////////////////////////////////////////////////////////// + + // TODO: remove from frameworks/base/core/res/AndroidManifest.xml once it's not used anymore + private static final String NOTIFICATION_INTENT = + "com.android.internal.autofill.action.REQUEST_AUTOFILL"; + private static final String EXTRA_USER_ID = "user_id"; + + private static final int MSG_SHOW_ALL_NOTIFICATIONS = 42; + private static final int SHOW_ALL_NOTIFICATIONS_DELAY_MS = 5000; + + private BroadcastReceiver mNotificationReceiver; + + final class NotificationReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final int userId = intent.getIntExtra(EXTRA_USER_ID, -1); + if (DEBUG) Slog.d(TAG, "Requesting autofill by notification for user " + userId); + synchronized (mLock) { + final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId); + if (service == null) { + Slog.w(TAG, "no auto-fill service for user " + userId); + } else { + service.requestAutoFill(null); + } } } } + + private ComponentName getProviderForUser(int userId) { + ComponentName serviceComponent = null; + ServiceInfo serviceInfo = null; + final String componentName = Settings.Secure.getStringForUser( + mResolver, Settings.Secure.AUTO_FILL_SERVICE, userId); + if (!TextUtils.isEmpty(componentName)) { + try { + serviceComponent = ComponentName.unflattenFromString(componentName); + serviceInfo = + AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, userId); + } catch (RuntimeException | RemoteException e) { + Slog.wtf(TAG, "Bad auto-fill service name " + componentName, e); + return null; + } + } + + if (DEBUG) Slog.d(TAG, "getServiceComponentForUser(" + userId + "): component=" + + serviceComponent + ", info: " + serviceInfo); + if (serviceInfo == null) { + Slog.w(TAG, "no service info for " + serviceComponent); + return null; + } + return serviceComponent; + } + + private void showAllNotifications() { + final UserManager userManager = + (UserManager) mContext.getSystemService(Context.USER_SERVICE); + + final List<UserInfo> allUsers = userManager.getUsers(true); + + for (UserInfo user : allUsers) { + final ComponentName serviceComponent = getProviderForUser(user.id); + if (serviceComponent != null) { + showNotification(serviceComponent, user.id); + } + } + } + + private void showNotification(ComponentName serviceComponent, int userId) { + if (DEBUG) Log.d(TAG, "showNotification() for " + userId + ": " + serviceComponent); + + synchronized (mLock) { + if (mNotificationReceiver == null) { + mNotificationReceiver = new NotificationReceiver(); + mContext.registerReceiver(mNotificationReceiver, + new IntentFilter(NOTIFICATION_INTENT)); + } + } + + final Intent intent = new Intent(NOTIFICATION_INTENT); + intent.putExtra(EXTRA_USER_ID, userId); + final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, + PendingIntent.FLAG_UPDATE_CURRENT); + + final String packageName = serviceComponent.getPackageName(); + String providerName = null; + final PackageManager pm = mContext.getPackageManager(); + try { + final ApplicationInfo info = pm.getApplicationInfoAsUser(packageName, 0, userId); + if (info != null) { + providerName = pm.getApplicationLabel(info).toString(); + } + } catch (Exception e) { + providerName = packageName; + } + final String title = "AutoFill by '" + providerName + "'"; + final String subTitle = "Tap notification to auto-fill top activity for user " + userId; + + final Notification notification = new Notification.Builder(mContext) + .setCategory(Notification.CATEGORY_SYSTEM) + .setOngoing(true) + .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb) + .setLocalOnly(true) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)) + .setContentTitle(title) + .setStyle(new Notification.BigTextStyle().bigText(subTitle)) + .setContentIntent(pi) + .build(); + NotificationManager.from(mContext).notify(userId, notification); + } + + private void cancelNotificationLocked(int userId) { + if (DEBUG) Log.d(TAG, "cancelNotificationLocked(): " + userId); + NotificationManager.from(mContext).cancel(userId); + } + + ///////////////////////////////////////// + // End of temporary notification code. // + ///////////////////////////////////////// } |