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.Build; 34 import android.os.IBinder; 35 import android.os.ParcelableException; 36 import android.os.RemoteException; 37 import android.os.ResultReceiver; 38 import android.os.ServiceManager; 39 import android.view.IWindowManager; 40 41 import com.android.compatibility.common.util.PollingCheck; 42 43 import java.util.concurrent.ExecutionException; 44 import java.util.concurrent.TimeoutException; 45 46 /** 47 * This is a bound service used in conjunction with CloseSystemDialogsTest. 48 */ 49 public class CloseSystemDialogsTestService extends Service { 50 private static final String TAG = "CloseSystemDialogsTestService"; 51 private static final String NOTIFICATION_ACTION = TAG; 52 private static final String NOTIFICATION_CHANNEL_ID = "cts/" + TAG; 53 private static final Intent INTENT_ACSD = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 54 55 private final ICloseSystemDialogsTestsService mBinder = new Binder(); 56 private NotificationManager mNotificationManager; 57 private IWindowManager mWindowManager; 58 private IActivityManager mActivityManager; 59 private BroadcastReceiver mNotificationReceiver; 60 61 @Override onCreate()62 public void onCreate() { 63 super.onCreate(); 64 mNotificationManager = getSystemService(NotificationManager.class); 65 mWindowManager = IWindowManager.Stub.asInterface( 66 ServiceManager.getService(Context.WINDOW_SERVICE)); 67 mActivityManager = IActivityManager.Stub.asInterface( 68 ServiceManager.getService(Context.ACTIVITY_SERVICE)); 69 } 70 71 @Override onBind(Intent intent)72 public IBinder onBind(Intent intent) { 73 return mBinder.asBinder(); 74 } 75 76 @Override onDestroy()77 public void onDestroy() { 78 super.onDestroy(); 79 if (mNotificationReceiver != null) { 80 unregisterReceiver(mNotificationReceiver); 81 } 82 } 83 84 private class Binder extends ICloseSystemDialogsTestsService.Stub { 85 private final Context mContext = CloseSystemDialogsTestService.this; 86 87 /** Checks that it can call @hide methods. */ 88 @Override waitUntilReady(long timeoutMs)89 public boolean waitUntilReady(long timeoutMs) { 90 try { 91 PollingCheck.check("Can't call @hide methods", timeoutMs, () -> { 92 try { 93 // Any method suffices since IWindowManager is @hide 94 mWindowManager.isRotationFrozen(); 95 return true; 96 } catch (NoSuchMethodError e) { 97 return false; 98 } 99 }); 100 return true; 101 } catch (AssertionError e) { 102 return false; 103 } catch (Exception e) { 104 throw new RuntimeException(e); 105 } 106 } 107 108 @Override sendCloseSystemDialogsBroadcast()109 public void sendCloseSystemDialogsBroadcast() { 110 mContext.sendBroadcast(INTENT_ACSD); 111 } 112 113 @Override postNotification(int notificationId, ResultReceiver receiver, boolean usePendingIntent)114 public void postNotification(int notificationId, ResultReceiver receiver, 115 boolean usePendingIntent) { 116 mNotificationReceiver = new BroadcastReceiver() { 117 @Override 118 public void onReceive(Context context, Intent intent) { 119 try { 120 if (usePendingIntent) { 121 PendingIntent.getBroadcast(mContext, /* requestCode */ 0, INTENT_ACSD, 122 FLAG_IMMUTABLE).send(); 123 } else { 124 mContext.sendBroadcast(INTENT_ACSD); 125 } 126 receiver.send(RESULT_OK, null); 127 } catch (SecurityException e) { 128 receiver.send(RESULT_SECURITY_EXCEPTION, null); 129 } catch (PendingIntent.CanceledException e) { 130 receiver.send(RESULT_ERROR, null); 131 } 132 } 133 }; 134 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 135 mContext.registerReceiver(mNotificationReceiver, 136 new IntentFilter(NOTIFICATION_ACTION), Context.RECEIVER_EXPORTED_UNAUDITED); 137 } else { 138 mContext.registerReceiver(mNotificationReceiver, 139 new IntentFilter(NOTIFICATION_ACTION)); 140 } 141 Intent intent = new Intent(NOTIFICATION_ACTION); 142 intent.setPackage(mContext.getPackageName()); 143 CloseSystemDialogsTestService.this.notify( 144 notificationId, 145 PendingIntent.getBroadcast(mContext, 0, intent, FLAG_IMMUTABLE)); 146 } 147 148 @Override closeSystemDialogsViaWindowManager(String reason)149 public void closeSystemDialogsViaWindowManager(String reason) throws RemoteException { 150 mWindowManager.closeSystemDialogs(reason); 151 } 152 153 @Override closeSystemDialogsViaActivityManager(String reason)154 public void closeSystemDialogsViaActivityManager(String reason) throws RemoteException { 155 mActivityManager.closeSystemDialogs(reason); 156 } 157 158 @Override waitForAccessibilityServiceWindow(long timeoutMs)159 public boolean waitForAccessibilityServiceWindow(long timeoutMs) throws RemoteException { 160 final AppAccessibilityService service; 161 try { 162 service = AppAccessibilityService.getConnected().get(timeoutMs, MILLISECONDS); 163 } catch (TimeoutException e) { 164 return false; 165 } catch (ExecutionException | InterruptedException e) { 166 throw new ParcelableException(e); 167 } 168 return service.waitWindowAdded(timeoutMs); 169 } 170 } 171 notify(int notificationId, PendingIntent intent)172 private void notify(int notificationId, PendingIntent intent) { 173 Notification notification = 174 new Notification.Builder(this, NOTIFICATION_CHANNEL_ID) 175 .setSmallIcon(android.R.drawable.ic_info) 176 .setContentIntent(intent) 177 .build(); 178 NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, 179 NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT); 180 mNotificationManager.createNotificationChannel(notificationChannel); 181 mNotificationManager.notify(notificationId, notification); 182 } 183 } 184