1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app.stubs.shared; 18 19 import static android.app.PendingIntent.FLAG_IMMUTABLE; 20 21 import static java.util.concurrent.TimeUnit.MILLISECONDS; 22 23 import android.app.IActivityManager; 24 import android.app.Notification; 25 import android.app.NotificationChannel; 26 import android.app.NotificationManager; 27 import android.app.PendingIntent; 28 import android.app.Service; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.os.IBinder; 34 import android.os.ParcelableException; 35 import android.os.RemoteException; 36 import android.os.ResultReceiver; 37 import android.os.ServiceManager; 38 import android.view.IWindowManager; 39 40 import com.android.compatibility.common.util.PollingCheck; 41 42 import java.util.concurrent.ExecutionException; 43 import java.util.concurrent.TimeoutException; 44 45 /** 46 * This is a bound service used in conjunction with CloseSystemDialogsTest. 47 */ 48 public class CloseSystemDialogsTestService extends Service { 49 private static final String TAG = "CloseSystemDialogsTestService"; 50 private static final String NOTIFICATION_ACTION = TAG; 51 private static final String NOTIFICATION_CHANNEL_ID = "cts/" + TAG; 52 private static final Intent INTENT_ACSD = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 53 54 private final ICloseSystemDialogsTestsService mBinder = new Binder(); 55 private NotificationManager mNotificationManager; 56 private IWindowManager mWindowManager; 57 private IActivityManager mActivityManager; 58 private BroadcastReceiver mNotificationReceiver; 59 60 @Override onCreate()61 public void onCreate() { 62 super.onCreate(); 63 mNotificationManager = getSystemService(NotificationManager.class); 64 mWindowManager = IWindowManager.Stub.asInterface( 65 ServiceManager.getService(Context.WINDOW_SERVICE)); 66 mActivityManager = IActivityManager.Stub.asInterface( 67 ServiceManager.getService(Context.ACTIVITY_SERVICE)); 68 } 69 70 @Override onBind(Intent intent)71 public IBinder onBind(Intent intent) { 72 return mBinder.asBinder(); 73 } 74 75 @Override onDestroy()76 public void onDestroy() { 77 super.onDestroy(); 78 if (mNotificationReceiver != null) { 79 unregisterReceiver(mNotificationReceiver); 80 } 81 } 82 83 private class Binder extends ICloseSystemDialogsTestsService.Stub { 84 private final Context mContext = CloseSystemDialogsTestService.this; 85 86 /** Checks that it can call @hide methods. */ 87 @Override waitUntilReady(long timeoutMs)88 public boolean waitUntilReady(long timeoutMs) { 89 try { 90 PollingCheck.check("Can't call @hide methods", timeoutMs, () -> { 91 try { 92 // Any method suffices since IWindowManager is @hide 93 mWindowManager.isRotationFrozen(); 94 return true; 95 } catch (NoSuchMethodError e) { 96 return false; 97 } 98 }); 99 return true; 100 } catch (AssertionError e) { 101 return false; 102 } catch (Exception e) { 103 throw new RuntimeException(e); 104 } 105 } 106 107 @Override sendCloseSystemDialogsBroadcast()108 public void sendCloseSystemDialogsBroadcast() { 109 mContext.sendBroadcast(INTENT_ACSD); 110 } 111 112 @Override postNotification(int notificationId, ResultReceiver receiver, boolean usePendingIntent)113 public void postNotification(int notificationId, ResultReceiver receiver, 114 boolean usePendingIntent) { 115 mNotificationReceiver = new BroadcastReceiver() { 116 @Override 117 public void onReceive(Context context, Intent intent) { 118 try { 119 if (usePendingIntent) { 120 PendingIntent.getBroadcast(mContext, /* requestCode */ 0, INTENT_ACSD, 121 FLAG_IMMUTABLE).send(); 122 } else { 123 mContext.sendBroadcast(INTENT_ACSD); 124 } 125 receiver.send(RESULT_OK, null); 126 } catch (SecurityException e) { 127 receiver.send(RESULT_SECURITY_EXCEPTION, null); 128 } catch (PendingIntent.CanceledException e) { 129 receiver.send(RESULT_ERROR, null); 130 } 131 } 132 }; 133 mContext.registerReceiver(mNotificationReceiver, new IntentFilter(NOTIFICATION_ACTION)); 134 Intent intent = new Intent(NOTIFICATION_ACTION); 135 intent.setPackage(mContext.getPackageName()); 136 CloseSystemDialogsTestService.this.notify( 137 notificationId, 138 PendingIntent.getBroadcast(mContext, 0, intent, FLAG_IMMUTABLE)); 139 } 140 141 @Override closeSystemDialogsViaWindowManager(String reason)142 public void closeSystemDialogsViaWindowManager(String reason) throws RemoteException { 143 mWindowManager.closeSystemDialogs(reason); 144 } 145 146 @Override closeSystemDialogsViaActivityManager(String reason)147 public void closeSystemDialogsViaActivityManager(String reason) throws RemoteException { 148 mActivityManager.closeSystemDialogs(reason); 149 } 150 151 @Override waitForAccessibilityServiceWindow(long timeoutMs)152 public boolean waitForAccessibilityServiceWindow(long timeoutMs) throws RemoteException { 153 final AppAccessibilityService service; 154 try { 155 service = AppAccessibilityService.getConnected().get(timeoutMs, MILLISECONDS); 156 } catch (TimeoutException e) { 157 return false; 158 } catch (ExecutionException | InterruptedException e) { 159 throw new ParcelableException(e); 160 } 161 return service.waitWindowAdded(timeoutMs); 162 } 163 } 164 notify(int notificationId, PendingIntent intent)165 private void notify(int notificationId, PendingIntent intent) { 166 Notification notification = 167 new Notification.Builder(this, NOTIFICATION_CHANNEL_ID) 168 .setSmallIcon(android.R.drawable.ic_info) 169 .setContentIntent(intent) 170 .build(); 171 NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, 172 NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT); 173 mNotificationManager.createNotificationChannel(notificationChannel); 174 mNotificationManager.notify(notificationId, notification); 175 } 176 } 177