1 /*
2  * Copyright (C) 2017 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 com.android.server.cts.device.statsdatom;
18 
19 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 import static com.google.common.truth.Truth.assertWithMessage;
23 
24 import static org.junit.Assert.assertNotNull;
25 
26 import android.accounts.Account;
27 import android.accounts.AccountManager;
28 import android.app.ActivityManager;
29 import android.app.ActivityManager.RunningServiceInfo;
30 import android.app.AppOpsManager;
31 import android.app.GameManager;
32 import android.app.GameModeConfiguration;
33 import android.app.GameState;
34 import android.app.job.JobInfo;
35 import android.app.job.JobScheduler;
36 import android.app.usage.NetworkStatsManager;
37 import android.bluetooth.le.BluetoothLeScanner;
38 import android.bluetooth.le.ScanCallback;
39 import android.bluetooth.le.ScanFilter;
40 import android.bluetooth.le.ScanResult;
41 import android.bluetooth.le.ScanSettings;
42 import android.bluetooth.test_utils.BlockingBluetoothAdapter;
43 import android.bluetooth.test_utils.EnableBluetoothRule;
44 import android.content.BroadcastReceiver;
45 import android.content.ComponentName;
46 import android.content.ContentResolver;
47 import android.content.Context;
48 import android.content.Intent;
49 import android.content.IntentFilter;
50 import android.content.pm.ApplicationInfo;
51 import android.hardware.camera2.CameraCharacteristics;
52 import android.hardware.camera2.CameraDevice;
53 import android.hardware.camera2.CameraManager;
54 import android.location.GnssStatus;
55 import android.location.Location;
56 import android.location.LocationListener;
57 import android.location.LocationManager;
58 import android.media.MediaDrm;
59 import android.media.MediaPlayer;
60 import android.net.ConnectivityManager;
61 import android.net.Network;
62 import android.net.NetworkCapabilities;
63 import android.net.NetworkRequest;
64 import android.net.cts.util.CtsNetUtils;
65 import android.net.wifi.WifiManager;
66 import android.os.AsyncTask;
67 import android.os.Bundle;
68 import android.os.Handler;
69 import android.os.HandlerThread;
70 import android.os.Looper;
71 import android.os.PowerManager;
72 import android.os.Process;
73 import android.os.RemoteException;
74 import android.os.SystemClock;
75 import android.os.VibrationEffect;
76 import android.os.Vibrator;
77 import android.provider.Settings;
78 import android.text.TextUtils;
79 import android.util.Log;
80 import android.util.StatsEvent;
81 import android.util.StatsLog;
82 
83 import androidx.annotation.NonNull;
84 import androidx.test.InstrumentationRegistry;
85 import androidx.test.ext.junit.runners.AndroidJUnit4;
86 
87 import com.android.compatibility.common.util.PollingCheck;
88 import com.android.compatibility.common.util.ShellIdentityUtils;
89 
90 import libcore.javax.net.ssl.TestSSLContext;
91 import libcore.javax.net.ssl.TestSSLSocketPair;
92 
93 import org.junit.Assert;
94 import org.junit.ClassRule;
95 import org.junit.Test;
96 import org.junit.runner.RunWith;
97 
98 import java.net.HttpURLConnection;
99 import java.net.URL;
100 import java.util.Arrays;
101 import java.util.List;
102 import java.util.UUID;
103 import java.util.concurrent.CountDownLatch;
104 import java.util.concurrent.TimeUnit;
105 
106 import javax.net.ssl.SSLSocket;
107 
108 @RunWith(AndroidJUnit4.class)
109 public class AtomTests {
110     @ClassRule
111     public static final EnableBluetoothRule sEnableBluetoothRule = new EnableBluetoothRule();
112 
113     private static final String TAG = AtomTests.class.getSimpleName();
114 
115     private static final String MY_PACKAGE_NAME = "com.android.server.cts.device.statsdatom";
116 
117     @Test
testTlsHandshake()118     public void testTlsHandshake() throws Exception {
119         TestSSLContext context = TestSSLContext.create();
120         SSLSocket[] sockets = TestSSLSocketPair.connect(context, null, null);
121 
122         if (sockets.length < 2) {
123             return;
124         }
125         sockets[0].getOutputStream().write(42);
126         Assert.assertEquals(42, sockets[1].getInputStream().read());
127         sockets[0].close();
128         sockets[1].close();
129     }
130 
131     @Test
132     // Start the isolated service, which logs an AppBreadcrumbReported atom, and then exit.
testIsolatedProcessService()133     public void testIsolatedProcessService() throws Exception {
134         Context context = InstrumentationRegistry.getContext();
135         Intent intent = new Intent(context, IsolatedProcessService.class);
136         context.startService(intent);
137         sleep(2_000);
138         context.stopService(intent);
139     }
140 
141     @Test
testAudioState()142     public void testAudioState() {
143         // TODO: This should surely be getTargetContext(), here and everywhere, but test first.
144         Context context = InstrumentationRegistry.getContext();
145         MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.good);
146         mediaPlayer.start();
147         sleep(2_000);
148         mediaPlayer.stop();
149     }
150 
151     @Test
testBleScanOpportunistic()152     public void testBleScanOpportunistic() {
153         ScanSettings scanSettings = new ScanSettings.Builder()
154                 .setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC).build();
155         performBleScan(scanSettings, null, false);
156     }
157 
158     @Test
testBleScanUnoptimized()159     public void testBleScanUnoptimized() {
160         ScanSettings scanSettings = new ScanSettings.Builder()
161                 .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
162         performBleScan(scanSettings, null, false);
163     }
164 
165     @Test
testBleScanResult()166     public void testBleScanResult() {
167         ScanSettings scanSettings = new ScanSettings.Builder()
168                 .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
169         ScanFilter.Builder scanFilter = new ScanFilter.Builder();
170         performBleScan(scanSettings, Arrays.asList(scanFilter.build()), true);
171     }
172 
173     @Test
testBleScanInterrupted()174     public void testBleScanInterrupted() throws Exception {
175         BluetoothLeScanner bleScanner = sEnableBluetoothRule.mAdapter.getBluetoothLeScanner();
176         assertThat(bleScanner).isNotNull();
177         ScanSettings scanSettings = new ScanSettings.Builder()
178                 .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
179         ScanCallback scanCallback = new ScanCallback() {
180             @Override
181             public void onScanResult(int callbackType, ScanResult result) {
182                 Log.v(TAG, "called onScanResult");
183             }
184 
185             @Override
186             public void onScanFailed(int errorCode) {
187                 Log.v(TAG, "called onScanFailed");
188             }
189 
190             @Override
191             public void onBatchScanResults(List<ScanResult> results) {
192                 Log.v(TAG, "called onBatchScanResults");
193             }
194         };
195 
196         int uid = Process.myUid();
197         int whatAtomId = 9_999;
198 
199         // Get the current setting for bluetooth background scanning.
200         // Set to 0 if the setting is not found or an error occurs.
201         int initialBleScanGlobalSetting = Settings.Global.getInt(
202                 InstrumentationRegistry.getTargetContext().getContentResolver(),
203                 Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
204 
205         // Turn off bluetooth background scanning.
206         Settings.Global.putInt(InstrumentationRegistry.getTargetContext().getContentResolver(),
207                 Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
208 
209         // Change state to State.ON.
210         bleScanner.startScan(null, scanSettings, scanCallback);
211         sleep(6_000);
212         writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
213         writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
214 
215         assertThat(BlockingBluetoothAdapter.disable(true)).isTrue();
216         assertThat(BlockingBluetoothAdapter.enable()).isTrue();
217 
218         writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
219         writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
220         writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
221 
222         // Set bluetooth background scanning to original setting.
223         Settings.Global.putInt(InstrumentationRegistry.getTargetContext().getContentResolver(),
224                 Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, initialBleScanGlobalSetting);
225     }
226 
writeSliceByBleScanStateChangedAtom(int atomId, int firstUid, boolean field2, boolean field3, boolean field4)227     private static void writeSliceByBleScanStateChangedAtom(int atomId, int firstUid,
228             boolean field2, boolean field3,
229             boolean field4) {
230         final StatsEvent.Builder builder = StatsEvent.newBuilder()
231                 .setAtomId(atomId)
232                 .writeAttributionChain(new int[]{firstUid}, new String[]{"tag1"})
233                 .writeBoolean(field2)
234                 .writeBoolean(field3)
235                 .writeBoolean(field4)
236                 .usePooledBuffer();
237 
238         StatsLog.write(builder.build());
239     }
240 
performBleScan(ScanSettings scanSettings, List<ScanFilter> scanFilters, boolean waitForResult)241     private static void performBleScan(ScanSettings scanSettings, List<ScanFilter> scanFilters,
242             boolean waitForResult) {
243         BluetoothLeScanner bleScanner = sEnableBluetoothRule.mAdapter.getBluetoothLeScanner();
244         assertThat(bleScanner).isNotNull();
245         CountDownLatch resultsLatch = new CountDownLatch(1);
246         ScanCallback scanCallback = new ScanCallback() {
247             @Override
248             public void onScanResult(int callbackType, ScanResult result) {
249                 Log.v(TAG, "called onScanResult");
250                 resultsLatch.countDown();
251             }
252 
253             @Override
254             public void onScanFailed(int errorCode) {
255                 Log.v(TAG, "called onScanFailed");
256             }
257 
258             @Override
259             public void onBatchScanResults(List<ScanResult> results) {
260                 Log.v(TAG, "called onBatchScanResults");
261                 resultsLatch.countDown();
262             }
263         };
264 
265         bleScanner.startScan(scanFilters, scanSettings, scanCallback);
266         if (waitForResult) {
267             waitForReceiver(InstrumentationRegistry.getContext(), 59_000, resultsLatch, null);
268         } else {
269             sleep(2_000);
270         }
271         bleScanner.stopScan(scanCallback);
272     }
273 
274     @Test
testCameraState()275     public void testCameraState() throws Exception {
276         Context context = InstrumentationRegistry.getContext();
277         CameraManager cam = context.getSystemService(CameraManager.class);
278         String[] cameraIds = cam.getCameraIdList();
279         if (cameraIds.length == 0) {
280             Log.e(TAG, "No camera found on device");
281             return;
282         }
283 
284         CountDownLatch latch = new CountDownLatch(1);
285         final CameraDevice.StateCallback cb = new CameraDevice.StateCallback() {
286             @Override
287             public void onOpened(CameraDevice cd) {
288                 Log.i(TAG, "CameraDevice " + cd.getId() + " opened");
289                 sleep(2_000);
290                 cd.close();
291             }
292 
293             @Override
294             public void onClosed(CameraDevice cd) {
295                 latch.countDown();
296                 Log.i(TAG, "CameraDevice " + cd.getId() + " closed");
297             }
298 
299             @Override
300             public void onDisconnected(CameraDevice cd) {
301                 Log.w(TAG, "CameraDevice  " + cd.getId() + " disconnected");
302             }
303 
304             @Override
305             public void onError(CameraDevice cd, int error) {
306                 Log.e(TAG, "CameraDevice " + cd.getId() + "had error " + error);
307             }
308         };
309 
310         HandlerThread handlerThread = new HandlerThread("br_handler_thread");
311         handlerThread.start();
312         Looper looper = handlerThread.getLooper();
313         Handler handler = new Handler(looper);
314 
315         cam.openCamera(cameraIds[0], cb, handler);
316         waitForReceiver(context, 10_000, latch, null);
317     }
318 
319     @Test
testFlashlight()320     public void testFlashlight() throws Exception {
321         Context context = InstrumentationRegistry.getContext();
322         CameraManager cam = context.getSystemService(CameraManager.class);
323         String[] cameraIds = cam.getCameraIdList();
324         boolean foundFlash = false;
325         for (int i = 0; i < cameraIds.length; i++) {
326             String id = cameraIds[i];
327             if (cam.getCameraCharacteristics(id).get(CameraCharacteristics.FLASH_INFO_AVAILABLE)) {
328                 cam.setTorchMode(id, true);
329                 sleep(500);
330                 cam.setTorchMode(id, false);
331                 foundFlash = true;
332                 break;
333             }
334         }
335         if (!foundFlash) {
336             Log.e(TAG, "No flashlight found on device");
337         }
338     }
339 
340     @Test
testForegroundService()341     public void testForegroundService() throws Exception {
342         Context context = InstrumentationRegistry.getContext();
343         // The service goes into foreground and exits shortly
344         Intent intent = new Intent(context, StatsdCtsForegroundService.class);
345         context.startService(intent);
346         sleep(500);
347         context.stopService(intent);
348     }
349 
350     @Test
testForegroundServiceAccessAppOp()351     public void testForegroundServiceAccessAppOp() throws Exception {
352         Context context = InstrumentationRegistry.getContext();
353         Intent fgsIntent = new Intent(context, StatsdCtsForegroundService.class);
354         AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
355 
356         // No foreground service session
357         noteAppOp(appOpsManager, AppOpsManager.OPSTR_COARSE_LOCATION);
358         sleep(500);
359 
360         // Foreground service session 1
361         context.startService(fgsIntent);
362         while (!checkIfServiceRunning(context, StatsdCtsForegroundService.class.getName())) {
363             sleep(50);
364         }
365         noteAppOp(appOpsManager, AppOpsManager.OPSTR_CAMERA);
366         noteAppOp(appOpsManager, AppOpsManager.OPSTR_FINE_LOCATION);
367         noteAppOp(appOpsManager, AppOpsManager.OPSTR_CAMERA);
368         startAppOp(appOpsManager, AppOpsManager.OPSTR_RECORD_AUDIO);
369         noteAppOp(appOpsManager, AppOpsManager.OPSTR_RECORD_AUDIO);
370         startAppOp(appOpsManager, AppOpsManager.OPSTR_CAMERA);
371         sleep(500);
372         context.stopService(fgsIntent);
373 
374         // No foreground service session
375         noteAppOp(appOpsManager, AppOpsManager.OPSTR_COARSE_LOCATION);
376         sleep(500);
377 
378         // TODO(b/149098800): Start fgs a second time and log OPSTR_CAMERA again
379     }
380 
381     @Test
testAppOps()382     public void testAppOps() throws Exception {
383         Context context = InstrumentationRegistry.getContext();
384         AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
385         String[] opsList = AppOpsManager.getOpStrs();
386 
387         for (int i = 0; i < opsList.length; i++) {
388             String op = opsList[i];
389             if (TextUtils.isEmpty(op) || op.startsWith("android:deprecated")) {
390                 // Operation removed/deprecated
391                 continue;
392             }
393             try {
394                 noteAppOp(appOpsManager, opsList[i]);
395             } catch (SecurityException e) {
396             }
397         }
398     }
399 
noteAppOp(AppOpsManager aom, String opStr)400     private void noteAppOp(AppOpsManager aom, String opStr) {
401         aom.noteOp(opStr, android.os.Process.myUid(), MY_PACKAGE_NAME, null, "statsdTest");
402     }
403 
startAppOp(AppOpsManager aom, String opStr)404     private void startAppOp(AppOpsManager aom, String opStr) {
405         aom.startOp(opStr, android.os.Process.myUid(), MY_PACKAGE_NAME, null, "statsdTest");
406     }
407 
408     /** Check if service is running. */
checkIfServiceRunning(Context context, String serviceName)409     public boolean checkIfServiceRunning(Context context, String serviceName) {
410         ActivityManager manager = context.getSystemService(ActivityManager.class);
411         for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
412             if (serviceName.equals(service.service.getClassName()) && service.foreground) {
413                 return true;
414             }
415         }
416         return false;
417     }
418 
419     @Test
testGpsScan()420     public void testGpsScan() {
421         Context context = InstrumentationRegistry.getContext();
422         final LocationManager locManager = context.getSystemService(LocationManager.class);
423         if (!locManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
424             Log.e(TAG, "GPS provider is not enabled");
425             return;
426         }
427         CountDownLatch latch = new CountDownLatch(1);
428 
429         final LocationListener locListener = new LocationListener() {
430             public void onLocationChanged(Location location) {
431                 Log.v(TAG, "onLocationChanged: location has been obtained");
432             }
433 
434             public void onProviderDisabled(String provider) {
435                 Log.w(TAG, "onProviderDisabled " + provider);
436             }
437 
438             public void onProviderEnabled(String provider) {
439                 Log.w(TAG, "onProviderEnabled " + provider);
440             }
441 
442             public void onStatusChanged(String provider, int status, Bundle extras) {
443                 Log.w(TAG, "onStatusChanged " + provider + " " + status);
444             }
445         };
446 
447         new AsyncTask<Void, Void, Void>() {
448             @Override
449             protected Void doInBackground(Void... params) {
450                 Looper.prepare();
451                 locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 990, 0,
452                         locListener);
453                 sleep(1_000);
454                 locManager.removeUpdates(locListener);
455                 latch.countDown();
456                 return null;
457             }
458         }.execute();
459 
460         waitForReceiver(context, 59_000, latch, null);
461     }
462 
463     @Test
testGpsStatus()464     public void testGpsStatus() {
465         Context context = InstrumentationRegistry.getContext();
466         final LocationManager locManager = context.getSystemService(LocationManager.class);
467 
468         if (!locManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
469             Log.e(TAG, "GPS provider is not enabled");
470             return;
471         }
472 
473         // Time out set to 85 seconds (5 seconds for sleep and a possible 85 seconds if TTFF takes
474         // max time which would be around 90 seconds.
475         // This is based on similar location cts test timeout values.
476         final int TIMEOUT_IN_MSEC = 85_000;
477         final int SLEEP_TIME_IN_MSEC = 5_000;
478 
479         final CountDownLatch mLatchNetwork = new CountDownLatch(1);
480 
481         final LocationListener locListener = location -> {
482             Log.v(TAG, "onLocationChanged: location has been obtained");
483             mLatchNetwork.countDown();
484         };
485 
486         // fetch the networklocation first to make sure the ttff is not flaky
487         if (locManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
488             Log.i(TAG, "Request Network Location updates.");
489             locManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
490                     0 /* minTime*/,
491                     0 /* minDistance */,
492                     locListener,
493                     Looper.getMainLooper());
494         }
495         waitForReceiver(context, TIMEOUT_IN_MSEC, mLatchNetwork, null);
496 
497         // TTFF could take up to 90 seconds, thus we need to wait till TTFF does occur if it does
498         // not occur in the first SLEEP_TIME_IN_MSEC
499         final CountDownLatch mLatchTtff = new CountDownLatch(1);
500 
501         GnssStatus.Callback gnssStatusCallback = new GnssStatus.Callback() {
502             @Override
503             public void onStarted() {
504                 Log.v(TAG, "Gnss Status Listener Started");
505             }
506 
507             @Override
508             public void onStopped() {
509                 Log.v(TAG, "Gnss Status Listener Stopped");
510             }
511 
512             @Override
513             public void onFirstFix(int ttffMillis) {
514                 Log.v(TAG, "Gnss Status Listener Received TTFF");
515                 mLatchTtff.countDown();
516             }
517 
518             @Override
519             public void onSatelliteStatusChanged(GnssStatus status) {
520                 Log.v(TAG, "Gnss Status Listener Received Status Update");
521             }
522         };
523 
524         boolean gnssStatusCallbackAdded = locManager.registerGnssStatusCallback(
525                 gnssStatusCallback, new Handler(Looper.getMainLooper()));
526         if (!gnssStatusCallbackAdded) {
527             // Registration of GnssMeasurements listener has failed, this indicates a platform bug.
528             Log.e(TAG, "Failed to start gnss status callback");
529         }
530 
531         locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
532                 0,
533                 0 /* minDistance */,
534                 locListener,
535                 Looper.getMainLooper());
536         sleep(SLEEP_TIME_IN_MSEC);
537         waitForReceiver(context, TIMEOUT_IN_MSEC, mLatchTtff, null);
538         locManager.removeUpdates(locListener);
539         locManager.unregisterGnssStatusCallback(gnssStatusCallback);
540     }
541 
542     @Test
testScreenBrightness()543     public void testScreenBrightness() {
544         Context context = InstrumentationRegistry.getContext();
545         PowerManager pm = context.getSystemService(PowerManager.class);
546         PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK |
547                 PowerManager.ACQUIRE_CAUSES_WAKEUP, "StatsdBrightnessTest");
548         wl.acquire();
549         sleep(500);
550 
551         setScreenBrightness(47);
552         sleep(500);
553         setScreenBrightness(100);
554         sleep(500);
555 
556 
557         wl.release();
558     }
559 
560     @Test
testSyncState()561     public void testSyncState() throws Exception {
562 
563         Context context = InstrumentationRegistry.getContext();
564         StatsdAuthenticator.removeAllAccounts(context);
565         AccountManager am = context.getSystemService(AccountManager.class);
566         CountDownLatch latch = StatsdSyncAdapter.resetCountDownLatch();
567 
568         Account account = StatsdAuthenticator.getTestAccount();
569         StatsdAuthenticator.ensureTestAccount(context);
570         sleep(500);
571 
572         // Just force set is syncable.
573         ContentResolver.setMasterSyncAutomatically(true);
574         sleep(500);
575         ContentResolver.setIsSyncable(account, StatsdProvider.AUTHORITY, 1);
576         // Wait for the first (automatic) sync to finish
577         waitForReceiver(context, 120_000, latch, null);
578 
579         //Sleep for 500ms, since we assert each start/stop to be ~500ms apart.
580         sleep(500);
581 
582         // Request and wait for the second sync to finish
583         latch = StatsdSyncAdapter.resetCountDownLatch();
584         StatsdSyncAdapter.requestSync(account);
585         waitForReceiver(context, 120_000, latch, null);
586         StatsdAuthenticator.removeAllAccounts(context);
587     }
588 
589     @Test
testScheduledJob()590     public void testScheduledJob() throws Exception {
591         final ComponentName name = new ComponentName(MY_PACKAGE_NAME,
592                 StatsdJobService.class.getName());
593 
594         Context context = InstrumentationRegistry.getContext();
595         JobScheduler js = context.getSystemService(JobScheduler.class);
596         assertWithMessage("JobScheduler service not available").that(js).isNotNull();
597 
598         JobInfo.Builder builder = new JobInfo.Builder(1, name);
599         builder.setOverrideDeadline(0);
600         JobInfo job = builder.build();
601 
602         CountDownLatch latch = StatsdJobService.resetCountDownLatch();
603         js.schedule(job);
604         waitForReceiver(context, 5_000, latch, null);
605     }
606 
607     @Test
testScheduledJob_CancelledJob()608     public void testScheduledJob_CancelledJob() throws Exception {
609         final ComponentName name = new ComponentName(MY_PACKAGE_NAME,
610                 StatsdJobService.class.getName());
611 
612         Context context = InstrumentationRegistry.getContext();
613         JobScheduler js = context.getSystemService(JobScheduler.class);
614         assertWithMessage("JobScheduler service not available").that(js).isNotNull();
615 
616         JobInfo.Builder builder = new JobInfo.Builder(1, name);
617         builder.setMinimumLatency(60_000L);
618         JobInfo job = builder.build();
619 
620         js.schedule(job);
621         js.cancel(1);
622     }
623 
624     @Test
testScheduledJobPriority()625     public void testScheduledJobPriority() throws Exception {
626         final ComponentName name =
627                 new ComponentName(MY_PACKAGE_NAME, StatsdJobService.class.getName());
628 
629         Context context = InstrumentationRegistry.getContext();
630         JobScheduler js = context.getSystemService(JobScheduler.class);
631         assertWithMessage("JobScheduler service not available").that(js).isNotNull();
632 
633         final int[] priorities = {
634                 JobInfo.PRIORITY_HIGH, JobInfo.PRIORITY_DEFAULT,
635                 JobInfo.PRIORITY_LOW, JobInfo.PRIORITY_MIN};
636         for (int priority : priorities) {
637             JobInfo job = new JobInfo.Builder(priority, name)
638                     .setOverrideDeadline(0)
639                     .setPriority(priority)
640                     .build();
641 
642             CountDownLatch latch = StatsdJobService.resetCountDownLatch();
643             js.schedule(job);
644             waitForReceiver(context, 5_000, latch, null);
645         }
646     }
647 
648     @Test
testVibratorState()649     public void testVibratorState() {
650         Context context = InstrumentationRegistry.getContext();
651         Vibrator vib = context.getSystemService(Vibrator.class);
652         if (vib.hasVibrator()) {
653             vib.vibrate(VibrationEffect.createOneShot(
654                     500 /* ms */, VibrationEffect.DEFAULT_AMPLITUDE));
655         }
656         // Sleep so that the app does not get killed.
657         sleep(1000);
658     }
659 
660     @Test
testWakelockState()661     public void testWakelockState() {
662         Context context = InstrumentationRegistry.getContext();
663         PowerManager pm = context.getSystemService(PowerManager.class);
664         PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
665                 "StatsdPartialWakelock");
666         wl.acquire();
667         sleep(500);
668         wl.release();
669     }
670 
671     @Test
testSliceByWakelockState()672     public void testSliceByWakelockState() {
673         int uid = Process.myUid();
674         int whatAtomId = 9_998;
675         int wakelockType = PowerManager.PARTIAL_WAKE_LOCK;
676         String tag = "StatsdPartialWakelock";
677 
678         Context context = InstrumentationRegistry.getContext();
679         PowerManager pm = context.getSystemService(PowerManager.class);
680         PowerManager.WakeLock wl = pm.newWakeLock(wakelockType, tag);
681 
682         wl.acquire();
683         sleep(500);
684         writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
685         writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
686         wl.acquire();
687         sleep(500);
688         writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
689         writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
690         writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
691         wl.release();
692         sleep(500);
693         writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
694         wl.release();
695         sleep(500);
696         writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
697         writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
698         writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
699     }
700 
writeSliceByWakelockStateChangedAtom(int atomId, int firstUid, int field2, String field3)701     private static void writeSliceByWakelockStateChangedAtom(int atomId, int firstUid,
702             int field2, String field3) {
703         final StatsEvent.Builder builder = StatsEvent.newBuilder()
704                 .setAtomId(atomId)
705                 .writeAttributionChain(new int[]{firstUid}, new String[]{"tag1"})
706                 .writeInt(field2)
707                 .writeString(field3)
708                 .usePooledBuffer();
709 
710         StatsLog.write(builder.build());
711     }
712 
713     @Test
testWakelockLoad()714     public void testWakelockLoad() {
715         final int NUM_THREADS = 16;
716         CountDownLatch latch = new CountDownLatch(NUM_THREADS);
717         for (int i = 0; i < NUM_THREADS; i++) {
718             Thread t = new Thread(new WakelockLoadTestRunnable("StatsdPartialWakelock" + i, latch));
719             t.start();
720         }
721         waitForReceiver(null, 120_000, latch, null);
722     }
723 
724     @Test
testWifiLockHighPerf()725     public void testWifiLockHighPerf() throws Exception {
726         Context context = InstrumentationRegistry.getContext();
727         boolean wifiConnected = isWifiConnected(context);
728         Assert.assertTrue(
729                 "Wifi is not connected. The test expects Wifi to be connected before the run",
730                 wifiConnected);
731 
732         WifiManager wm = context.getSystemService(WifiManager.class);
733         WifiManager.WifiLock lock =
734                 wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "StatsdCTSWifiLock");
735         lock.acquire();
736         sleep(500);
737         lock.release();
738     }
739 
740     @Test
testWifiConnected()741     public void testWifiConnected() throws Exception {
742         Context context = InstrumentationRegistry.getContext();
743         boolean wifiConnected = isWifiConnected(context);
744         Assert.assertTrue(
745                 "Wifi is not connected. The test expects Wifi to be connected before the run",
746                 wifiConnected);
747     }
748 
749     @Test
testWifiMulticastLock()750     public void testWifiMulticastLock() {
751         Context context = InstrumentationRegistry.getContext();
752         WifiManager wm = context.getSystemService(WifiManager.class);
753         WifiManager.MulticastLock lock = wm.createMulticastLock("StatsdCTSMulticastLock");
754         lock.acquire();
755         sleep(500);
756         lock.release();
757     }
758 
759     @Test
760     /** Does two wifi scans. */
761     // TODO: Copied this from BatterystatsValidation but we probably don't need to wait for results.
testWifiScan()762     public void testWifiScan() {
763         Context context = InstrumentationRegistry.getContext();
764         IntentFilter intentFilter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
765         // Sometimes a scan was already running (from a different uid), so the first scan doesn't
766         // start when requested. Therefore, additionally wait for whatever scan is currently running
767         // to finish, then request a scan again - at least one of these two scans should be
768         // attributed to this app.
769         for (int i = 0; i < 2; i++) {
770             CountDownLatch onReceiveLatch = new CountDownLatch(1);
771             BroadcastReceiver receiver = registerReceiver(context, onReceiveLatch, intentFilter);
772             context.getSystemService(WifiManager.class).startScan();
773             waitForReceiver(context, 60_000, onReceiveLatch, receiver);
774         }
775     }
776 
777     @Test
testWifiReconnect()778     public void testWifiReconnect() throws Exception {
779         Context context = InstrumentationRegistry.getContext();
780         boolean wifiConnected = isWifiConnected(context);
781         Assert.assertTrue(
782                 "Wifi is not connected. The test expects Wifi to be connected before the run",
783                 wifiConnected);
784 
785         wifiDisconnect(context);
786         sleep(500);
787         wifiReconnect(context);
788         sleep(500);
789     }
790 
791     @Test
testSimpleCpu()792     public void testSimpleCpu() {
793         long timestamp = System.currentTimeMillis();
794         for (int i = 0; i < 10000; i++) {
795             timestamp += i;
796         }
797         Log.i(TAG, "The answer is " + timestamp);
798     }
799 
800     @Test
testWriteRawTestAtom()801     public void testWriteRawTestAtom() throws Exception {
802         Context context = InstrumentationRegistry.getTargetContext();
803         ApplicationInfo appInfo = context.getPackageManager()
804                 .getApplicationInfo(context.getPackageName(), 0);
805         int[] uids = {1234, appInfo.uid};
806         String[] tags = {"tag1", "tag2"};
807         byte[] experimentIds = {8, 1, 8, 2, 8, 3}; // Corresponds to 1, 2, 3.
808 
809         int[] int32Array = {3, 6};
810         long[] int64Array = {1000L, 1002L};
811         float[] floatArray = {0.3f, 0.09f};
812         String[] stringArray = {"str1", "str2"};
813         boolean[] boolArray = {true, false};
814         int[] enumArray = {StatsLogStatsdCts.TEST_ATOM_REPORTED__STATE__OFF,
815                 StatsLogStatsdCts.TEST_ATOM_REPORTED__STATE__ON};
816 
817         StatsLogStatsdCts.write(StatsLogStatsdCts.TEST_ATOM_REPORTED, uids, tags, 42,
818                 Long.MAX_VALUE, 3.14f, "This is a basic test!", false,
819                 StatsLogStatsdCts.TEST_ATOM_REPORTED__STATE__ON, experimentIds, int32Array,
820                 int64Array, floatArray, stringArray, boolArray, enumArray);
821 
822         // All nulls. Should get dropped since cts app is not in the attribution chain.
823         StatsLogStatsdCts.write(StatsLogStatsdCts.TEST_ATOM_REPORTED, null, null, 0, 0, 0f, null,
824                 false, StatsLogStatsdCts.TEST_ATOM_REPORTED__STATE__ON, null, null, null, null,
825                 null, null, null);
826 
827         // Null tag in attribution chain.
828         int[] uids2 = {9999, appInfo.uid};
829         String[] tags2 = {"tag9999", null};
830         StatsLogStatsdCts.write(StatsLogStatsdCts.TEST_ATOM_REPORTED, uids2, tags2, 100,
831                 Long.MIN_VALUE, -2.5f, "Test null uid", true,
832                 StatsLogStatsdCts.TEST_ATOM_REPORTED__STATE__UNKNOWN, experimentIds, int32Array,
833                 int64Array, floatArray, stringArray, boolArray, enumArray);
834 
835         // Non chained non-null
836         StatsLogStatsdCts.write_non_chained(StatsLogStatsdCts.TEST_ATOM_REPORTED, appInfo.uid,
837                 "tag1", -256, -1234567890L, 42.01f, "Test non chained", true,
838                 StatsLogStatsdCts.TEST_ATOM_REPORTED__STATE__OFF, experimentIds, new int[0],
839                 new long[0], new float[0], new String[0], new boolean[0], new int[0]);
840 
841         // Non chained all null
842         StatsLogStatsdCts.write_non_chained(StatsLogStatsdCts.TEST_ATOM_REPORTED, appInfo.uid, null,
843                 0, 0, 0f, null, true, StatsLogStatsdCts.TEST_ATOM_REPORTED__STATE__OFF, null, null,
844                 null, null, null, null, null);
845     }
846 
847     @Test
testWriteExtensionTestAtom()848     public void testWriteExtensionTestAtom() throws Exception {
849         Context context = InstrumentationRegistry.getTargetContext();
850         ApplicationInfo appInfo = context.getPackageManager()
851                 .getApplicationInfo(context.getPackageName(), 0);
852         int[] uids = {1234, appInfo.uid};
853         String[] tags = {"tag1", "tag2"};
854         byte[] testAtomNestedMsg = {8, 1, 8, 2, 8, 3}; // Corresponds to 1, 2, 3.
855 
856         int[] int32Array = {3, 6};
857         long[] int64Array = {1000L, 1002L};
858         float[] floatArray = {0.3f, 0.09f};
859         String[] stringArray = {"str1", "str2"};
860         boolean[] boolArray = {true, false};
861         int[] enumArray = {StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED__STATE__OFF,
862                 StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED__STATE__ON};
863 
864         StatsLogStatsdCts.write(StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED, uids, tags, 42,
865                 Long.MAX_VALUE, 3.14f, "This is a basic test!", false,
866                 StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED__STATE__ON, testAtomNestedMsg,
867                 int32Array,
868                 int64Array, floatArray, stringArray, boolArray, enumArray);
869 
870         // All nulls. Should get dropped since cts app is not in the attribution chain.
871         StatsLogStatsdCts.write(StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED, null, null, 0, 0,
872                 0f, null,
873                 false, StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED__STATE__ON, null, null, null,
874                 null,
875                 null, null, null);
876 
877         // Null tag in attribution chain.
878         int[] uids2 = {9999, appInfo.uid};
879         String[] tags2 = {"tag9999", null};
880         StatsLogStatsdCts.write(StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED, uids2, tags2, 100,
881                 Long.MIN_VALUE, -2.5f, "Test null uid", true,
882                 StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED__STATE__UNKNOWN, testAtomNestedMsg,
883                 int32Array,
884                 int64Array, floatArray, stringArray, boolArray, enumArray);
885 
886         // Non chained non-null
887         StatsLogStatsdCts.write_non_chained(StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED,
888                 appInfo.uid,
889                 "tag1", -256, -1234567890L, 42.01f, "Test non chained", true,
890                 StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED__STATE__OFF, testAtomNestedMsg,
891                 new int[0],
892                 new long[0], new float[0], new String[0], new boolean[0], new int[0]);
893 
894         // Non chained all null
895         StatsLogStatsdCts.write_non_chained(StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED,
896                 appInfo.uid, null,
897                 0, 0, 0f, null, true, StatsLogStatsdCts.TEST_EXTENSION_ATOM_REPORTED__STATE__OFF,
898                 null, null,
899                 null, null, null, null, null);
900     }
901 
902     /**
903      * Bring up and generate some traffic on cellular data connection.
904      */
905     @Test
testGenerateMobileTraffic()906     public void testGenerateMobileTraffic() throws Exception {
907         final Context context = InstrumentationRegistry.getContext();
908         doGenerateNetworkTraffic(context, NetworkCapabilities.TRANSPORT_CELLULAR);
909     }
910 
911     /**
912      * Force poll NetworkStatsService to get most updated network stats from lower layer.
913      */
914     @Test
testForcePollNetworkStats()915     public void testForcePollNetworkStats() throws Exception {
916         final Context context = InstrumentationRegistry.getContext();
917         final NetworkStatsManager nsm = context.getSystemService(NetworkStatsManager.class);
918         try {
919             nsm.setPollForce(true);
920             // This query is for triggering force poll NetworkStatsService.
921             nsm.querySummaryForUser(ConnectivityManager.TYPE_WIFI, null, Long.MIN_VALUE,
922                     Long.MAX_VALUE);
923         } catch (RemoteException e) {
924             Log.e(TAG, "doPollNetworkStats failed with " + e);
925         }
926     }
927 
928     // Constants which are locally used by doGenerateNetworkTraffic.
929     private static final int NETWORK_TIMEOUT_MILLIS = 15000;
930     private static final String HTTPS_HOST_URL =
931             "https://connectivitycheck.gstatic.com/generate_204";
932 
doGenerateNetworkTraffic(@onNull Context context, int transport)933     private void doGenerateNetworkTraffic(@NonNull Context context, int transport)
934             throws InterruptedException {
935         final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
936         final NetworkRequest request = new NetworkRequest.Builder().addCapability(
937                 NetworkCapabilities.NET_CAPABILITY_INTERNET).addTransportType(transport).build();
938         final CtsNetUtils.TestNetworkCallback callback = new CtsNetUtils.TestNetworkCallback();
939 
940         // Request network, and make http query when the network is available.
941         cm.requestNetwork(request, callback);
942 
943         // If network is not available, throws IllegalStateException.
944         final Network network = callback.waitForAvailable();
945         if (network == null) {
946             throw new IllegalStateException("network with transport " + transport
947                     + " is not available.");
948         }
949 
950         final long startTime = SystemClock.elapsedRealtime();
951         try {
952             exerciseRemoteHost(cm, network, new URL(HTTPS_HOST_URL));
953             Log.i(TAG, "exerciseRemoteHost successful in " + (SystemClock.elapsedRealtime()
954                     - startTime) + " ms");
955         } catch (Exception e) {
956             Log.e(TAG, "exerciseRemoteHost failed in " + (SystemClock.elapsedRealtime()
957                     - startTime) + " ms: " + e);
958         } finally {
959             cm.unregisterNetworkCallback(callback);
960         }
961     }
962 
963     /**
964      * Generate traffic on specified network.
965      */
exerciseRemoteHost(@onNull ConnectivityManager cm, @NonNull Network network, @NonNull URL url)966     private void exerciseRemoteHost(@NonNull ConnectivityManager cm, @NonNull Network network,
967             @NonNull URL url) throws Exception {
968         cm.bindProcessToNetwork(network);
969         HttpURLConnection urlc = null;
970         try {
971             urlc = (HttpURLConnection) network.openConnection(url);
972             urlc.setConnectTimeout(NETWORK_TIMEOUT_MILLIS);
973             urlc.setUseCaches(false);
974             urlc.connect();
975         } finally {
976             if (urlc != null) {
977                 urlc.disconnect();
978             }
979         }
980     }
981 
982     // ------- Helper methods
983 
984     /** Puts the current thread to sleep. */
sleep(int millis)985     static void sleep(int millis) {
986         try {
987             Thread.sleep(millis);
988         } catch (InterruptedException e) {
989             Log.e(TAG, "Interrupted exception while sleeping", e);
990         }
991     }
992 
993     /** Register receiver to determine when given action is complete. */
registerReceiver( Context ctx, CountDownLatch onReceiveLatch, IntentFilter intentFilter)994     private static BroadcastReceiver registerReceiver(
995             Context ctx, CountDownLatch onReceiveLatch, IntentFilter intentFilter) {
996         BroadcastReceiver receiver = new BroadcastReceiver() {
997             @Override
998             public void onReceive(Context context, Intent intent) {
999                 Log.d(TAG, "Received broadcast.");
1000                 onReceiveLatch.countDown();
1001             }
1002         };
1003         // Run Broadcast receiver in a different thread since the main thread will wait.
1004         HandlerThread handlerThread = new HandlerThread("br_handler_thread");
1005         handlerThread.start();
1006         Looper looper = handlerThread.getLooper();
1007         Handler handler = new Handler(looper);
1008         ctx.registerReceiver(receiver, intentFilter, null, handler);
1009         return receiver;
1010     }
1011 
1012     /**
1013      * Uses the receiver to wait until the action is complete. ctx and receiver may be null if no
1014      * receiver is needed to be unregistered.
1015      */
waitForReceiver(Context ctx, int maxWaitTimeMs, CountDownLatch latch, BroadcastReceiver receiver)1016     private static void waitForReceiver(Context ctx,
1017             int maxWaitTimeMs, CountDownLatch latch, BroadcastReceiver receiver) {
1018         try {
1019             boolean didFinish = latch.await(maxWaitTimeMs, TimeUnit.MILLISECONDS);
1020             if (didFinish) {
1021                 Log.v(TAG, "Finished performing action");
1022             } else {
1023                 // This is not necessarily a problem. If we just want to make sure a count was
1024                 // recorded for the request, it doesn't matter if the action actually finished.
1025                 Log.w(TAG, "Did not finish in specified time.");
1026             }
1027         } catch (InterruptedException e) {
1028             Log.e(TAG, "Interrupted exception while awaiting action to finish", e);
1029         }
1030         if (ctx != null && receiver != null) {
1031             ctx.unregisterReceiver(receiver);
1032         }
1033     }
1034 
setScreenBrightness(int brightness)1035     private static void setScreenBrightness(int brightness) {
1036         runShellCommand("settings put system screen_brightness " + brightness);
1037     }
1038 
1039     private static final int WIFI_CONNECT_TIMEOUT_MILLIS = 30_000;
1040 
wifiDisconnect(Context context)1041     public void wifiDisconnect(Context context) throws Exception {
1042         WifiManager wifiManager = context.getSystemService(WifiManager.class);
1043         ShellIdentityUtils.invokeWithShellPermissions(() -> wifiManager.disconnect());
1044 
1045         PollingCheck.check(
1046                 "Timed out waiting for Wifi to become disconnected",
1047                 WIFI_CONNECT_TIMEOUT_MILLIS,
1048                 () -> !isWifiConnected(context));
1049     }
1050 
wifiReconnect(Context context)1051     public void wifiReconnect(Context context) throws Exception {
1052         WifiManager wifiManager = context.getSystemService(WifiManager.class);
1053         ShellIdentityUtils.invokeWithShellPermissions(() -> wifiManager.reconnect());
1054 
1055         PollingCheck.check(
1056                 "Timed out waiting for Wifi to become connected",
1057                 WIFI_CONNECT_TIMEOUT_MILLIS,
1058                 () -> isWifiConnected(context));
1059     }
1060 
isWifiConnected(Context context)1061     private boolean isWifiConnected(Context context) throws Exception {
1062         ConnectivityManager connManager = context.getSystemService(ConnectivityManager.class);
1063         if (connManager == null) {
1064             return false;
1065         }
1066 
1067         Network[] networks = connManager.getAllNetworks();
1068         for (Network network : networks) {
1069             if (network == null) {
1070                 continue;
1071             }
1072 
1073             NetworkCapabilities caps = connManager.getNetworkCapabilities(network);
1074             if (caps == null) {
1075                 continue;
1076             }
1077 
1078             if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
1079                 return true;
1080             }
1081         }
1082 
1083         return false;
1084     }
1085 
1086     @Test
testGameState()1087     public void testGameState() throws Exception {
1088         Context context = InstrumentationRegistry.getContext();
1089         GameManager gameManager = context.getSystemService(GameManager.class);
1090         gameManager.setGameState(new GameState(true, GameState.MODE_CONTENT, 1, 2));
1091     }
1092 
1093     @Test
testSetGameMode()1094     public void testSetGameMode() throws Exception {
1095         Context context = InstrumentationRegistry.getContext();
1096         GameManager gameManager = context.getSystemService(GameManager.class);
1097         assertNotNull(gameManager);
1098         assertNotNull(context.getPackageName());
1099         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(gameManager,
1100                 (gm) -> gm.setGameMode(context.getPackageName(),
1101                         GameManager.GAME_MODE_PERFORMANCE), "android.permission.MANAGE_GAME_MODE");
1102         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(gameManager,
1103                 (gm) -> gm.setGameMode(context.getPackageName(),
1104                         GameManager.GAME_MODE_BATTERY), "android.permission.MANAGE_GAME_MODE");
1105     }
1106 
1107     @Test
testUpdateCustomGameModeConfiguration()1108     public void testUpdateCustomGameModeConfiguration() throws Exception {
1109         Context context = InstrumentationRegistry.getContext();
1110         GameManager gameManager = context.getSystemService(GameManager.class);
1111         assertNotNull(gameManager);
1112         assertNotNull(context.getPackageName());
1113         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(gameManager,
1114                 (gm) -> gm.updateCustomGameModeConfiguration(context.getPackageName(),
1115                         new GameModeConfiguration.Builder()
1116                                 .setScalingFactor(0.5f)
1117                                 .setFpsOverride(30).build()));
1118         ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(gameManager,
1119                 (gm) -> gm.updateCustomGameModeConfiguration(context.getPackageName(),
1120                         new GameModeConfiguration.Builder()
1121                                 .setScalingFactor(0.9f)
1122                                 .setFpsOverride(60).build()));
1123     }
1124 
1125     @Test
testMediaDrmAtoms()1126     public void testMediaDrmAtoms() throws Exception {
1127         UUID clearKeyUuid = new UUID(0xe2719d58a985b3c9L, 0x781ab030af78d30eL);
1128         byte[] sid = null;
1129         final int OEM_ERROR = 123;
1130         final int ERROR_CONTEXT = 456;
1131         final int ANDROID_U = 14;
1132         try (MediaDrm drm = new MediaDrm(clearKeyUuid)) {
1133             if (getClearkeyVersionInt(drm) >= ANDROID_U) {
1134                 drm.setPropertyString("oemError", Integer.toString(OEM_ERROR));
1135                 drm.setPropertyString("errorContext", Integer.toString(ERROR_CONTEXT));
1136             }
1137             for (int i = 0; i < 2; i++) {
1138                 // Mock error is set per-session
1139                 drm.setPropertyString("drmErrorTest", "lostState");
1140                 sid = drm.openSession();
1141                 Assert.assertNotNull("null session id", sid);
1142                 try {
1143                     drm.closeSession(sid);
1144                 } catch (MediaDrm.MediaDrmStateException e) {
1145                     Log.d(TAG, "expected for lost state");
1146                 }
1147             }
1148         }
1149     }
1150 
getClearkeyVersionInt(MediaDrm drm)1151     private int getClearkeyVersionInt(MediaDrm drm) {
1152         try {
1153             return Integer.parseInt(drm.getPropertyString("version"));
1154         } catch (Exception e) {
1155             return Integer.MIN_VALUE;
1156         }
1157     }
1158 }
1159