• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.service.watchdog;
18 
19 import static android.os.Parcelable.Creator;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SdkConstant;
24 import android.annotation.SuppressLint;
25 import android.annotation.SystemApi;
26 import android.annotation.TestApi;
27 import android.app.Service;
28 import android.content.Intent;
29 import android.content.pm.PackageManager;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.os.IBinder;
33 import android.os.Looper;
34 import android.os.Parcel;
35 import android.os.Parcelable;
36 import android.os.RemoteCallback;
37 import android.os.RemoteException;
38 import android.util.Log;
39 
40 import com.android.internal.util.Preconditions;
41 
42 import java.util.ArrayList;
43 import java.util.List;
44 import java.util.Objects;
45 import java.util.concurrent.TimeUnit;
46 
47 /**
48  * A service to provide packages supporting explicit health checks and route checks to these
49  * packages on behalf of the package watchdog.
50  *
51  * <p>To extend this class, you must declare the service in your manifest file with the
52  * {@link android.Manifest.permission#BIND_EXPLICIT_HEALTH_CHECK_SERVICE} permission,
53  * and include an intent filter with the {@link #SERVICE_INTERFACE} action. In adddition,
54  * your implementation must live in
55  * {@link PackageManager#getServicesSystemSharedLibraryPackageName()}.
56  * For example:</p>
57  * <pre>
58  *     &lt;service android:name=".FooExplicitHealthCheckService"
59  *             android:exported="true"
60  *             android:priority="100"
61  *             android:permission="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"&gt;
62  *         &lt;intent-filter&gt;
63  *             &lt;action android:name="android.service.watchdog.ExplicitHealthCheckService" /&gt;
64  *         &lt;/intent-filter&gt;
65  *     &lt;/service&gt;
66  * </pre>
67  * @hide
68  */
69 @SystemApi
70 public abstract class ExplicitHealthCheckService extends Service {
71 
72     private static final String TAG = "ExplicitHealthCheckService";
73 
74     /**
75      * {@link Bundle} key for a {@link List} of {@link PackageConfig} value.
76      *
77      * {@hide}
78      */
79     public static final String EXTRA_SUPPORTED_PACKAGES =
80             "android.service.watchdog.extra.supported_packages";
81 
82     /**
83      * {@link Bundle} key for a {@link List} of {@link String} value.
84      *
85      * {@hide}
86      */
87     public static final String EXTRA_REQUESTED_PACKAGES =
88             "android.service.watchdog.extra.requested_packages";
89 
90     /**
91      * {@link Bundle} key for a {@link String} value.
92      *
93      * {@hide}
94      */
95     public static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE =
96             "android.service.watchdog.extra.health_check_passed_package";
97 
98     /**
99      * The Intent action that a service must respond to. Add it to the intent filter of the service
100      * in its manifest.
101      */
102     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
103     public static final String SERVICE_INTERFACE =
104             "android.service.watchdog.ExplicitHealthCheckService";
105 
106     /**
107      * The permission that a service must require to ensure that only Android system can bind to it.
108      * If this permission is not enforced in the AndroidManifest of the service, the system will
109      * skip that service.
110      */
111     public static final String BIND_PERMISSION =
112             "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
113 
114     private final ExplicitHealthCheckServiceWrapper mWrapper =
115             new ExplicitHealthCheckServiceWrapper();
116 
117     /**
118      * Called when the system requests an explicit health check for {@code packageName}.
119      *
120      * <p> When {@code packageName} passes the check, implementors should call
121      * {@link #notifyHealthCheckPassed} to inform the system.
122      *
123      * <p> It could take many hours before a {@code packageName} passes a check and implementors
124      * should never drop requests unless {@link onCancel} is called or the service dies.
125      *
126      * <p> Requests should not be queued and additional calls while expecting a result for
127      * {@code packageName} should have no effect.
128      */
onRequestHealthCheck(@onNull String packageName)129     public abstract void onRequestHealthCheck(@NonNull String packageName);
130 
131     /**
132      * Called when the system cancels the explicit health check request for {@code packageName}.
133      * Should do nothing if there are is no active request for {@code packageName}.
134      */
onCancelHealthCheck(@onNull String packageName)135     public abstract void onCancelHealthCheck(@NonNull String packageName);
136 
137     /**
138      * Called when the system requests for all the packages supporting explicit health checks. The
139      * system may request an explicit health check for any of these packages with
140      * {@link #onRequestHealthCheck}.
141      *
142      * @return all packages supporting explicit health checks
143      */
onGetSupportedPackages()144     @NonNull public abstract List<PackageConfig> onGetSupportedPackages();
145 
146     /**
147      * Called when the system requests for all the packages that it has currently requested
148      * an explicit health check for.
149      *
150      * @return all packages expecting an explicit health check result
151      */
onGetRequestedPackages()152     @NonNull public abstract List<String> onGetRequestedPackages();
153 
154     private final Handler mHandler = Handler.createAsync(Looper.getMainLooper());
155     @Nullable private RemoteCallback mCallback;
156 
157     @Override
158     @NonNull
onBind(@onNull Intent intent)159     public final IBinder onBind(@NonNull Intent intent) {
160         return mWrapper;
161     }
162 
163     /**
164      * Sets {@link RemoteCallback}, for testing purpose.
165      *
166      * @hide
167      */
168     @TestApi
setCallback(@ullable RemoteCallback callback)169     public void setCallback(@Nullable RemoteCallback callback) {
170         mCallback = callback;
171     }
172     /**
173      * Implementors should call this to notify the system when explicit health check passes
174      * for {@code packageName};
175      */
notifyHealthCheckPassed(@onNull String packageName)176     public final void notifyHealthCheckPassed(@NonNull String packageName) {
177         mHandler.post(() -> {
178             if (mCallback != null) {
179                 Objects.requireNonNull(packageName,
180                         "Package passing explicit health check must be non-null");
181                 Bundle bundle = new Bundle();
182                 bundle.putString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE, packageName);
183                 mCallback.sendResult(bundle);
184             } else {
185                 Log.wtf(TAG, "System missed explicit health check result for " + packageName);
186             }
187         });
188     }
189 
190     /**
191      * A PackageConfig contains a package supporting explicit health checks and the
192      * timeout in {@link System#uptimeMillis} across reboots after which health
193      * check requests from clients are failed.
194      *
195      * @hide
196      */
197     @SystemApi
198     public static final class PackageConfig implements Parcelable {
199         private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(1);
200 
201         private final String mPackageName;
202         private final long mHealthCheckTimeoutMillis;
203 
204         /**
205          * Creates a new instance.
206          *
207          * @param packageName the package name
208          * @param durationMillis the duration in milliseconds, must be greater than or
209          * equal to 0. If it is 0, it will use a system default value.
210          */
PackageConfig(@onNull String packageName, long healthCheckTimeoutMillis)211         public PackageConfig(@NonNull String packageName, long healthCheckTimeoutMillis) {
212             mPackageName = Preconditions.checkNotNull(packageName);
213             if (healthCheckTimeoutMillis == 0) {
214                 mHealthCheckTimeoutMillis = DEFAULT_HEALTH_CHECK_TIMEOUT_MILLIS;
215             } else {
216                 mHealthCheckTimeoutMillis = Preconditions.checkArgumentNonnegative(
217                         healthCheckTimeoutMillis);
218             }
219         }
220 
PackageConfig(Parcel parcel)221         private PackageConfig(Parcel parcel) {
222             mPackageName = parcel.readString();
223             mHealthCheckTimeoutMillis = parcel.readLong();
224         }
225 
226         /**
227          * Gets the package name.
228          *
229          * @return the package name
230          */
getPackageName()231         public @NonNull String getPackageName() {
232             return mPackageName;
233         }
234 
235         /**
236          * Gets the timeout in milliseconds to evaluate an explicit health check result after a
237          * request.
238          *
239          * @return the duration in {@link System#uptimeMillis} across reboots
240          */
getHealthCheckTimeoutMillis()241         public long getHealthCheckTimeoutMillis() {
242             return mHealthCheckTimeoutMillis;
243         }
244 
245         @NonNull
246         @Override
toString()247         public String toString() {
248             return "PackageConfig{" + mPackageName + ", " + mHealthCheckTimeoutMillis + "}";
249         }
250 
251         @Override
equals(@ullable Object other)252         public boolean equals(@Nullable Object other) {
253             if (other == this) {
254                 return true;
255             }
256             if (!(other instanceof PackageConfig)) {
257                 return false;
258             }
259 
260             PackageConfig otherInfo = (PackageConfig) other;
261             return Objects.equals(otherInfo.getHealthCheckTimeoutMillis(),
262                     mHealthCheckTimeoutMillis)
263                     && Objects.equals(otherInfo.getPackageName(), mPackageName);
264         }
265 
266         @Override
hashCode()267         public int hashCode() {
268             return Objects.hash(mPackageName, mHealthCheckTimeoutMillis);
269         }
270 
271         @Override
describeContents()272         public int describeContents() {
273             return 0;
274         }
275 
276         @Override
writeToParcel(@uppressLint{"MissingNullability"}) Parcel parcel, int flags)277         public void writeToParcel(@SuppressLint({"MissingNullability"}) Parcel parcel, int flags) {
278             parcel.writeString(mPackageName);
279             parcel.writeLong(mHealthCheckTimeoutMillis);
280         }
281 
282         public static final @NonNull Creator<PackageConfig> CREATOR = new Creator<PackageConfig>() {
283                 @Override
284                 public PackageConfig createFromParcel(Parcel source) {
285                     return new PackageConfig(source);
286                 }
287 
288                 @Override
289                 public PackageConfig[] newArray(int size) {
290                     return new PackageConfig[size];
291                 }
292             };
293     }
294 
295 
296     private class ExplicitHealthCheckServiceWrapper extends IExplicitHealthCheckService.Stub {
297         @Override
setCallback(RemoteCallback callback)298         public void setCallback(RemoteCallback callback) throws RemoteException {
299             mHandler.post(() -> {
300                 mCallback = callback;
301             });
302         }
303 
304         @Override
request(String packageName)305         public void request(String packageName) throws RemoteException {
306             mHandler.post(() -> ExplicitHealthCheckService.this.onRequestHealthCheck(packageName));
307         }
308 
309         @Override
cancel(String packageName)310         public void cancel(String packageName) throws RemoteException {
311             mHandler.post(() -> ExplicitHealthCheckService.this.onCancelHealthCheck(packageName));
312         }
313 
314         @Override
getSupportedPackages(RemoteCallback callback)315         public void getSupportedPackages(RemoteCallback callback) throws RemoteException {
316             mHandler.post(() -> {
317                 List<PackageConfig> packages =
318                         ExplicitHealthCheckService.this.onGetSupportedPackages();
319                 Objects.requireNonNull(packages, "Supported package list must be non-null");
320                 Bundle bundle = new Bundle();
321                 bundle.putParcelableArrayList(EXTRA_SUPPORTED_PACKAGES, new ArrayList<>(packages));
322                 callback.sendResult(bundle);
323             });
324         }
325 
326         @Override
getRequestedPackages(RemoteCallback callback)327         public void getRequestedPackages(RemoteCallback callback) throws RemoteException {
328             mHandler.post(() -> {
329                 List<String> packages =
330                         ExplicitHealthCheckService.this.onGetRequestedPackages();
331                 Objects.requireNonNull(packages, "Requested  package list must be non-null");
332                 Bundle bundle = new Bundle();
333                 bundle.putStringArrayList(EXTRA_REQUESTED_PACKAGES, new ArrayList<>(packages));
334                 callback.sendResult(bundle);
335             });
336         }
337     }
338 }
339