1 /*
2  * Copyright (C) 2020 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.suspendapps.suspendtestapp;
18 
19 import android.app.Service;
20 import android.content.Intent;
21 import android.os.Bundle;
22 import android.os.IBinder;
23 import android.os.RemoteException;
24 import android.util.ArrayMap;
25 import android.util.Log;
26 
27 import androidx.annotation.GuardedBy;
28 
29 import java.util.concurrent.atomic.AtomicInteger;
30 
31 /**
32  * Service used by the test to investigate this app's state.
33  */
34 public class TestService extends Service {
35     private static final String TAG = TestService.class.getSimpleName();
36 
37     private static final Object sLock = new Object();
38     @GuardedBy("sLock")
39     private static final ArrayMap<String, IBroadcastReporter> sRegisteredReporters =
40             new ArrayMap<>();
41 
42     private final IBinder mBinder = new ITestService.Stub() {
43         @Override
44         public boolean isPackageSuspended() throws RemoteException {
45             return getPackageManager().isPackageSuspended();
46         }
47 
48         @Override
49         public Bundle getSuspendedAppExtras() throws RemoteException {
50             return getPackageManager().getSuspendedPackageAppExtras();
51         }
52 
53         @Override
54         public Intent waitForActivityStart(long timeoutMs) throws RemoteException {
55             synchronized (SuspendTestActivity.sLock) {
56                 if (SuspendTestActivity.sStartedIntent == null) {
57                     try {
58                         SuspendTestActivity.sLock.wait(timeoutMs);
59                     } catch (InterruptedException e) {
60                         Log.e(TAG, "Interrupted while waiting for activity start", e);
61                     }
62                 }
63                 return SuspendTestActivity.sStartedIntent;
64             }
65         }
66 
67         @Override
68         public boolean waitForActivityStop(long timeoutMs) throws RemoteException {
69             synchronized (SuspendTestActivity.sLock) {
70                 if (SuspendTestActivity.sStartedIntent != null) {
71                     try {
72                         SuspendTestActivity.sLock.wait(timeoutMs);
73                     } catch (InterruptedException e) {
74                         Log.e(TAG, "Interrupted while waiting for activity stop", e);
75                     }
76                 }
77                 return SuspendTestActivity.sStartedIntent == null;
78             }
79         }
80 
81         /**
82          * Registers a {@link com.android.suspendapps.suspendtestapp.IBroadcastReporter} to trigger
83          * whenever this app receives a broadcast with the specified Intent action.
84          *
85          * <p> This will replace any existing registration present for the given {@code action} and
86          * the {@code reporter} will get unregistered once it has been called.
87          *
88          * @param reporter
89          * @param action
90          * @throws RemoteException
91          */
92         @Override
93         public void registerBroadcastReporter(String action, IBroadcastReporter reporter)
94                 throws RemoteException {
95             synchronized (sLock) {
96                 sRegisteredReporters.put(action, reporter);
97             }
98         }
99 
100         @Override
101         public void unregisterBroadcastReporter(String action)
102                 throws RemoteException {
103             synchronized (sLock) {
104                 sRegisteredReporters.remove(action);
105             }
106         }
107     };
108 
reportBroadcastIfNeeded(Intent intent)109     static void reportBroadcastIfNeeded(Intent intent) {
110         IBroadcastReporter reporter;
111         synchronized (sLock) {
112             reporter = sRegisteredReporters.remove(intent.getAction());
113         }
114         if (reporter != null) {
115             try {
116                 reporter.onBroadcastReceived(intent);
117             } catch (RemoteException re) {
118                 Log.e(TAG, "Unable to report broadcast " + intent + " to instrumentation", re);
119             }
120         }
121     }
122 
123     private static final AtomicInteger sNumBindings = new AtomicInteger(0);
124 
125     @Override
onBind(Intent intent)126     public IBinder onBind(Intent intent) {
127         final int numBindings = sNumBindings.incrementAndGet();
128         if (numBindings > 1) {
129             Log.w(TAG, "Too many bindings: " + numBindings);
130         }
131         return mBinder;
132     }
133 
134     @Override
onUnbind(Intent intent)135     public boolean onUnbind(Intent intent) {
136         sNumBindings.getAndDecrement();
137         synchronized (sLock) {
138             sRegisteredReporters.clear();
139         }
140         return super.onUnbind(intent);
141     }
142 }
143