blob: 872d7237f4f116a3d2a3cd73fede77d5fa74e2a9 [file] [log] [blame]
Neil Fuller68f66662017-03-16 18:32:21 +00001/*
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
17package com.android.server.timezone;
18
19import com.android.internal.annotations.VisibleForTesting;
Neil Fullerd857f672017-07-20 11:00:35 +010020import com.android.server.EventLogTags;
Neil Fuller68f66662017-03-16 18:32:21 +000021import com.android.server.SystemService;
Neil Fullera6a71d02017-06-13 15:12:17 +010022import com.android.timezone.distro.DistroException;
23import com.android.timezone.distro.DistroVersion;
24import com.android.timezone.distro.StagedDistroOperation;
Neil Fuller54525bf2017-06-22 14:10:29 +010025import com.android.timezone.distro.TimeZoneDistro;
Neil Fuller5ca8b5b2017-06-29 12:39:49 +010026import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
Neil Fuller68f66662017-03-16 18:32:21 +000027
28import android.app.timezone.Callback;
29import android.app.timezone.DistroFormatVersion;
30import android.app.timezone.DistroRulesVersion;
31import android.app.timezone.ICallback;
32import android.app.timezone.IRulesManager;
33import android.app.timezone.RulesManager;
34import android.app.timezone.RulesState;
35import android.content.Context;
36import android.os.ParcelFileDescriptor;
37import android.os.RemoteException;
38import android.util.Slog;
39
40import java.io.File;
Neil Fuller87b11282017-06-23 16:43:45 +010041import java.io.FileDescriptor;
Neil Fuller54525bf2017-06-22 14:10:29 +010042import java.io.FileInputStream;
Neil Fuller68f66662017-03-16 18:32:21 +000043import java.io.IOException;
Neil Fuller54525bf2017-06-22 14:10:29 +010044import java.io.InputStream;
Neil Fuller87b11282017-06-23 16:43:45 +010045import java.io.PrintWriter;
Neil Fuller68f66662017-03-16 18:32:21 +000046import java.util.Arrays;
47import java.util.concurrent.Executor;
48import java.util.concurrent.atomic.AtomicBoolean;
Neil Fuller87b11282017-06-23 16:43:45 +010049import libcore.icu.ICU;
Neil Fullerc9036142017-10-04 17:04:38 +010050import libcore.util.TimeZoneFinder;
Neil Fuller87b11282017-06-23 16:43:45 +010051import libcore.util.ZoneInfoDB;
52
53import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
54import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
55import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN;
56import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL;
57import static android.app.timezone.RulesState.STAGED_OPERATION_NONE;
58import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL;
59import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +000060
Neil Fuller68f66662017-03-16 18:32:21 +000061public final class RulesManagerService extends IRulesManager.Stub {
62
63 private static final String TAG = "timezone.RulesManagerService";
64
65 /** The distro format supported by this device. */
66 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
67 static final DistroFormatVersion DISTRO_FORMAT_VERSION_SUPPORTED =
68 new DistroFormatVersion(
69 DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
70 DistroVersion.CURRENT_FORMAT_MINOR_VERSION);
71
72 public static class Lifecycle extends SystemService {
Neil Fuller68f66662017-03-16 18:32:21 +000073 public Lifecycle(Context context) {
74 super(context);
75 }
76
77 @Override
78 public void onStart() {
Neil Fullercd1a1092017-09-13 21:59:59 +010079 RulesManagerService service = RulesManagerService.create(getContext());
80 service.start();
Neil Fuller68f66662017-03-16 18:32:21 +000081
Neil Fullercd1a1092017-09-13 21:59:59 +010082 // Publish the binder service so it can be accessed from other (appropriately
83 // permissioned) processes.
84 publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, service);
85
86 // Publish the service instance locally so we can use it directly from within the system
87 // server from TimeZoneUpdateIdler.
88 publishLocalService(RulesManagerService.class, service);
Neil Fuller68f66662017-03-16 18:32:21 +000089 }
90 }
91
92 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
93 static final String REQUIRED_UPDATER_PERMISSION =
94 android.Manifest.permission.UPDATE_TIME_ZONE_RULES;
95 private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata");
96 private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
97
98 private final AtomicBoolean mOperationInProgress = new AtomicBoolean(false);
99 private final PermissionHelper mPermissionHelper;
100 private final PackageTracker mPackageTracker;
101 private final Executor mExecutor;
Neil Fullerb1442272017-12-18 15:59:50 +0000102 private final RulesManagerIntentHelper mIntentHelper;
Neil Fuller68f66662017-03-16 18:32:21 +0000103 private final TimeZoneDistroInstaller mInstaller;
Neil Fuller68f66662017-03-16 18:32:21 +0000104
105 private static RulesManagerService create(Context context) {
106 RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context);
107 return new RulesManagerService(
108 helper /* permissionHelper */,
109 helper /* executor */,
Neil Fullerb1442272017-12-18 15:59:50 +0000110 helper /* intentHelper */,
Neil Fuller68f66662017-03-16 18:32:21 +0000111 PackageTracker.create(context),
112 new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR));
113 }
114
115 // A constructor that can be used by tests to supply mocked / faked dependencies.
Neil Fullerb1442272017-12-18 15:59:50 +0000116 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
117 RulesManagerService(PermissionHelper permissionHelper, Executor executor,
118 RulesManagerIntentHelper intentHelper, PackageTracker packageTracker,
Neil Fuller68f66662017-03-16 18:32:21 +0000119 TimeZoneDistroInstaller timeZoneDistroInstaller) {
120 mPermissionHelper = permissionHelper;
121 mExecutor = executor;
Neil Fullerb1442272017-12-18 15:59:50 +0000122 mIntentHelper = intentHelper;
Neil Fuller68f66662017-03-16 18:32:21 +0000123 mPackageTracker = packageTracker;
124 mInstaller = timeZoneDistroInstaller;
125 }
126
127 public void start() {
Neil Fuller35822592017-12-11 14:39:03 +0000128 // Return value deliberately ignored: no action required on failure to start.
Neil Fuller68f66662017-03-16 18:32:21 +0000129 mPackageTracker.start();
130 }
131
132 @Override // Binder call
133 public RulesState getRulesState() {
134 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
135
Neil Fuller87b11282017-06-23 16:43:45 +0100136 return getRulesStateInternal();
137 }
138
139 /** Like {@link #getRulesState()} without the permission check. */
140 private RulesState getRulesStateInternal() {
Neil Fuller68f66662017-03-16 18:32:21 +0000141 synchronized(this) {
142 String systemRulesVersion;
143 try {
144 systemRulesVersion = mInstaller.getSystemRulesVersion();
145 } catch (IOException e) {
146 Slog.w(TAG, "Failed to read system rules", e);
147 return null;
148 }
149
Neil Fullerad3e1332018-01-22 12:17:14 +0000150 // Determine the installed distro state. This should be possible regardless of whether
151 // there's an operation in progress.
152 DistroVersion installedDistroVersion;
153 int distroStatus = DISTRO_STATUS_UNKNOWN;
154 DistroRulesVersion installedDistroRulesVersion = null;
155 try {
156 installedDistroVersion = mInstaller.getInstalledDistroVersion();
157 if (installedDistroVersion == null) {
158 distroStatus = DISTRO_STATUS_NONE;
159 installedDistroRulesVersion = null;
160 } else {
161 distroStatus = DISTRO_STATUS_INSTALLED;
162 installedDistroRulesVersion = new DistroRulesVersion(
163 installedDistroVersion.rulesVersion,
164 installedDistroVersion.revision);
165 }
166 } catch (DistroException | IOException e) {
167 Slog.w(TAG, "Failed to read installed distro.", e);
168 }
169
Neil Fuller68f66662017-03-16 18:32:21 +0000170 boolean operationInProgress = this.mOperationInProgress.get();
171
172 // Determine the staged operation status, if possible.
173 DistroRulesVersion stagedDistroRulesVersion = null;
Neil Fuller87b11282017-06-23 16:43:45 +0100174 int stagedOperationStatus = STAGED_OPERATION_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +0000175 if (!operationInProgress) {
176 StagedDistroOperation stagedDistroOperation;
177 try {
178 stagedDistroOperation = mInstaller.getStagedDistroOperation();
179 if (stagedDistroOperation == null) {
Neil Fuller87b11282017-06-23 16:43:45 +0100180 stagedOperationStatus = STAGED_OPERATION_NONE;
Neil Fuller68f66662017-03-16 18:32:21 +0000181 } else if (stagedDistroOperation.isUninstall) {
Neil Fuller87b11282017-06-23 16:43:45 +0100182 stagedOperationStatus = STAGED_OPERATION_UNINSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000183 } else {
184 // Must be an install.
Neil Fuller87b11282017-06-23 16:43:45 +0100185 stagedOperationStatus = STAGED_OPERATION_INSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000186 DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion;
187 stagedDistroRulesVersion = new DistroRulesVersion(
188 stagedDistroVersion.rulesVersion,
189 stagedDistroVersion.revision);
190 }
191 } catch (DistroException | IOException e) {
192 Slog.w(TAG, "Failed to read staged distro.", e);
193 }
194 }
Neil Fuller68f66662017-03-16 18:32:21 +0000195 return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
196 operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
197 distroStatus, installedDistroRulesVersion);
198 }
199 }
200
201 @Override
Neil Fuller54525bf2017-06-22 14:10:29 +0100202 public int requestInstall(ParcelFileDescriptor distroParcelFileDescriptor,
203 byte[] checkTokenBytes, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000204
Neil Fuller54525bf2017-06-22 14:10:29 +0100205 boolean closeParcelFileDescriptorOnExit = true;
206 try {
207 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
Neil Fuller68f66662017-03-16 18:32:21 +0000208
Neil Fuller54525bf2017-06-22 14:10:29 +0100209 CheckToken checkToken = null;
210 if (checkTokenBytes != null) {
211 checkToken = createCheckTokenOrThrow(checkTokenBytes);
212 }
Neil Fullerd857f672017-07-20 11:00:35 +0100213 EventLogTags.writeTimezoneRequestInstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000214
Neil Fuller54525bf2017-06-22 14:10:29 +0100215 synchronized (this) {
216 if (distroParcelFileDescriptor == null) {
217 throw new NullPointerException("distroParcelFileDescriptor == null");
218 }
219 if (callback == null) {
220 throw new NullPointerException("observer == null");
221 }
222 if (mOperationInProgress.get()) {
223 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
224 }
225 mOperationInProgress.set(true);
226
227 // Execute the install asynchronously.
228 mExecutor.execute(
229 new InstallRunnable(distroParcelFileDescriptor, checkToken, callback));
230
231 // The InstallRunnable now owns the ParcelFileDescriptor, so it will close it after
232 // it executes (and we do not have to).
233 closeParcelFileDescriptorOnExit = false;
234
235 return RulesManager.SUCCESS;
236 }
237 } finally {
238 // We should close() the local ParcelFileDescriptor we were passed if it hasn't been
239 // passed to another thread to handle.
240 if (distroParcelFileDescriptor != null && closeParcelFileDescriptorOnExit) {
241 try {
242 distroParcelFileDescriptor.close();
243 } catch (IOException e) {
244 Slog.w(TAG, "Failed to close distroParcelFileDescriptor", e);
245 }
246 }
Neil Fuller68f66662017-03-16 18:32:21 +0000247 }
248 }
249
250 private class InstallRunnable implements Runnable {
251
Neil Fuller54525bf2017-06-22 14:10:29 +0100252 private final ParcelFileDescriptor mDistroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000253 private final CheckToken mCheckToken;
254 private final ICallback mCallback;
255
Neil Fuller54525bf2017-06-22 14:10:29 +0100256 InstallRunnable(ParcelFileDescriptor distroParcelFileDescriptor, CheckToken checkToken,
257 ICallback callback) {
258 mDistroParcelFileDescriptor = distroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000259 mCheckToken = checkToken;
260 mCallback = callback;
261 }
262
263 @Override
264 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100265 EventLogTags.writeTimezoneInstallStarted(toStringOrNull(mCheckToken));
266
Neil Fuller54525bf2017-06-22 14:10:29 +0100267 boolean success = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000268 // Adopt the ParcelFileDescriptor into this try-with-resources so it is closed
269 // when we are done.
Neil Fuller54525bf2017-06-22 14:10:29 +0100270 try (ParcelFileDescriptor pfd = mDistroParcelFileDescriptor) {
271 // The ParcelFileDescriptor owns the underlying FileDescriptor and we'll close
272 // it at the end of the try-with-resources.
273 final boolean isFdOwner = false;
274 InputStream is = new FileInputStream(pfd.getFileDescriptor(), isFdOwner);
275
276 TimeZoneDistro distro = new TimeZoneDistro(is);
Neil Fullerfe3b1182017-06-19 12:56:08 +0100277 int installerResult = mInstaller.stageInstallWithErrorCode(distro);
Neil Fullerb1442272017-12-18 15:59:50 +0000278
279 // Notify interested parties that something is staged.
280 sendInstallNotificationIntentIfRequired(installerResult);
281
Neil Fuller68f66662017-03-16 18:32:21 +0000282 int resultCode = mapInstallerResultToApiCode(installerResult);
Neil Fullerd857f672017-07-20 11:00:35 +0100283 EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000284 sendFinishedStatus(mCallback, resultCode);
285
286 // All the installer failure modes are currently non-recoverable and won't be
287 // improved by trying again. Therefore success = true.
288 success = true;
289 } catch (Exception e) {
290 Slog.w(TAG, "Failed to install distro.", e);
Neil Fullerd857f672017-07-20 11:00:35 +0100291 EventLogTags.writeTimezoneInstallComplete(
292 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000293 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
294 } finally {
295 // Notify the package tracker that the operation is now complete.
296 mPackageTracker.recordCheckResult(mCheckToken, success);
297
298 mOperationInProgress.set(false);
299 }
300 }
301
Neil Fullerb1442272017-12-18 15:59:50 +0000302 private void sendInstallNotificationIntentIfRequired(int installerResult) {
303 if (installerResult == TimeZoneDistroInstaller.INSTALL_SUCCESS) {
304 mIntentHelper.sendTimeZoneOperationStaged();
305 }
306 }
307
Neil Fuller68f66662017-03-16 18:32:21 +0000308 private int mapInstallerResultToApiCode(int installerResult) {
309 switch (installerResult) {
310 case TimeZoneDistroInstaller.INSTALL_SUCCESS:
311 return Callback.SUCCESS;
312 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE:
313 return Callback.ERROR_INSTALL_BAD_DISTRO_STRUCTURE;
314 case TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD:
315 return Callback.ERROR_INSTALL_RULES_TOO_OLD;
316 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION:
317 return Callback.ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION;
318 case TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR:
319 return Callback.ERROR_INSTALL_VALIDATION_ERROR;
320 default:
321 return Callback.ERROR_UNKNOWN_FAILURE;
322 }
323 }
324 }
325
326 @Override
327 public int requestUninstall(byte[] checkTokenBytes, ICallback callback) {
328 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
329
330 CheckToken checkToken = null;
331 if (checkTokenBytes != null) {
332 checkToken = createCheckTokenOrThrow(checkTokenBytes);
333 }
Neil Fullerd857f672017-07-20 11:00:35 +0100334 EventLogTags.writeTimezoneRequestUninstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000335 synchronized(this) {
336 if (callback == null) {
337 throw new NullPointerException("callback == null");
338 }
339
340 if (mOperationInProgress.get()) {
341 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
342 }
343 mOperationInProgress.set(true);
344
345 // Execute the uninstall asynchronously.
346 mExecutor.execute(new UninstallRunnable(checkToken, callback));
347
348 return RulesManager.SUCCESS;
349 }
350 }
351
352 private class UninstallRunnable implements Runnable {
353
354 private final CheckToken mCheckToken;
355 private final ICallback mCallback;
356
Neil Fullera47c3632017-07-26 13:56:25 +0100357 UninstallRunnable(CheckToken checkToken, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000358 mCheckToken = checkToken;
359 mCallback = callback;
360 }
361
362 @Override
363 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100364 EventLogTags.writeTimezoneUninstallStarted(toStringOrNull(mCheckToken));
Neil Fuller8e27c922017-09-14 09:34:56 +0100365 boolean packageTrackerStatus = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000366 try {
Neil Fuller8e27c922017-09-14 09:34:56 +0100367 int uninstallResult = mInstaller.stageUninstall();
Neil Fullerb1442272017-12-18 15:59:50 +0000368
369 // Notify interested parties that something is staged.
370 sendUninstallNotificationIntentIfRequired(uninstallResult);
371
Neil Fuller8e27c922017-09-14 09:34:56 +0100372 packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS
373 || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
374
375 // Right now we just have Callback.SUCCESS / Callback.ERROR_UNKNOWN_FAILURE for
376 // uninstall. All clients should be checking against SUCCESS. More granular failures
377 // may be added in future.
378 int callbackResultCode =
379 packageTrackerStatus ? Callback.SUCCESS : Callback.ERROR_UNKNOWN_FAILURE;
Neil Fullerd857f672017-07-20 11:00:35 +0100380 EventLogTags.writeTimezoneUninstallComplete(
Neil Fuller8e27c922017-09-14 09:34:56 +0100381 toStringOrNull(mCheckToken), callbackResultCode);
382 sendFinishedStatus(mCallback, callbackResultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000383 } catch (Exception e) {
Neil Fullerd857f672017-07-20 11:00:35 +0100384 EventLogTags.writeTimezoneUninstallComplete(
385 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000386 Slog.w(TAG, "Failed to uninstall distro.", e);
387 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
388 } finally {
389 // Notify the package tracker that the operation is now complete.
Neil Fuller8e27c922017-09-14 09:34:56 +0100390 mPackageTracker.recordCheckResult(mCheckToken, packageTrackerStatus);
Neil Fuller68f66662017-03-16 18:32:21 +0000391
392 mOperationInProgress.set(false);
393 }
394 }
Neil Fullerb1442272017-12-18 15:59:50 +0000395
396 private void sendUninstallNotificationIntentIfRequired(int uninstallResult) {
397 switch (uninstallResult) {
398 case TimeZoneDistroInstaller.UNINSTALL_SUCCESS:
399 mIntentHelper.sendTimeZoneOperationStaged();
400 break;
401 case TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED:
402 mIntentHelper.sendTimeZoneOperationUnstaged();
403 break;
404 case TimeZoneDistroInstaller.UNINSTALL_FAIL:
405 default:
406 // No-op - unknown or nothing to notify about.
407 }
408 }
Neil Fuller68f66662017-03-16 18:32:21 +0000409 }
410
411 private void sendFinishedStatus(ICallback callback, int resultCode) {
412 try {
413 callback.onFinished(resultCode);
414 } catch (RemoteException e) {
415 Slog.e(TAG, "Unable to notify observer of result", e);
416 }
417 }
418
419 @Override
420 public void requestNothing(byte[] checkTokenBytes, boolean success) {
421 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
422 CheckToken checkToken = null;
423 if (checkTokenBytes != null) {
424 checkToken = createCheckTokenOrThrow(checkTokenBytes);
425 }
Neil Fullerd857f672017-07-20 11:00:35 +0100426 EventLogTags.writeTimezoneRequestNothing(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000427 mPackageTracker.recordCheckResult(checkToken, success);
Neil Fullerd857f672017-07-20 11:00:35 +0100428 EventLogTags.writeTimezoneNothingComplete(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000429 }
430
Neil Fuller87b11282017-06-23 16:43:45 +0100431 @Override
432 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
433 if (!mPermissionHelper.checkDumpPermission(TAG, pw)) {
434 return;
435 }
436
437 RulesState rulesState = getRulesStateInternal();
438 if (args != null && args.length == 2) {
439 // Formatting options used for automated tests. The format is less free-form than
440 // the -format options, which are intended to be easier to parse.
441 if ("-format_state".equals(args[0]) && args[1] != null) {
442 for (char c : args[1].toCharArray()) {
443 switch (c) {
Neil Fullera47c3632017-07-26 13:56:25 +0100444 case 'p': {
445 // Report operation in progress
446 String value = "Unknown";
447 if (rulesState != null) {
448 value = Boolean.toString(rulesState.isOperationInProgress());
Neil Fuller87b11282017-06-23 16:43:45 +0100449 }
Neil Fullera47c3632017-07-26 13:56:25 +0100450 pw.println("Operation in progress: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100451 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100452 }
453 case 's': {
454 // Report system image rules version
455 String value = "Unknown";
456 if (rulesState != null) {
457 value = rulesState.getSystemRulesVersion();
458 }
459 pw.println("System rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100460 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100461 }
462 case 'c': {
463 // Report current installation state
464 String value = "Unknown";
465 if (rulesState != null) {
466 value = distroStatusToString(rulesState.getDistroStatus());
467 }
468 pw.println("Current install state: " + value);
469 break;
470 }
471 case 'i': {
472 // Report currently installed version
473 String value = "Unknown";
474 if (rulesState != null) {
475 DistroRulesVersion installedRulesVersion =
476 rulesState.getInstalledDistroRulesVersion();
477 if (installedRulesVersion == null) {
478 value = "<None>";
479 } else {
480 value = installedRulesVersion.toDumpString();
481 }
482 }
483 pw.println("Installed rules version: " + value);
484 break;
485 }
486 case 'o': {
487 // Report staged operation type
488 String value = "Unknown";
489 if (rulesState != null) {
490 int stagedOperationType = rulesState.getStagedOperationType();
491 value = stagedOperationToString(stagedOperationType);
492 }
493 pw.println("Staged operation: " + value);
494 break;
495 }
496 case 't': {
Neil Fuller87b11282017-06-23 16:43:45 +0100497 // Report staged version (i.e. the one that will be installed next boot
498 // if the staged operation is an install).
Neil Fullera47c3632017-07-26 13:56:25 +0100499 String value = "Unknown";
500 if (rulesState != null) {
501 DistroRulesVersion stagedDistroRulesVersion =
502 rulesState.getStagedDistroRulesVersion();
503 if (stagedDistroRulesVersion == null) {
504 value = "<None>";
505 } else {
506 value = stagedDistroRulesVersion.toDumpString();
507 }
Neil Fuller87b11282017-06-23 16:43:45 +0100508 }
Neil Fullera47c3632017-07-26 13:56:25 +0100509 pw.println("Staged rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100510 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100511 }
512 case 'a': {
Neil Fuller87b11282017-06-23 16:43:45 +0100513 // Report the active rules version (i.e. the rules in use by the current
514 // process).
Neil Fullerc9036142017-10-04 17:04:38 +0100515 pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
Neil Fuller87b11282017-06-23 16:43:45 +0100516 + ICU.getTZDataVersion() + ","
Neil Fullerc9036142017-10-04 17:04:38 +0100517 + ZoneInfoDB.getInstance().getVersion() + ","
518 + TimeZoneFinder.getInstance().getIanaVersion());
Neil Fuller87b11282017-06-23 16:43:45 +0100519 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100520 }
521 default: {
Neil Fuller87b11282017-06-23 16:43:45 +0100522 pw.println("Unknown option: " + c);
Neil Fullera47c3632017-07-26 13:56:25 +0100523 }
Neil Fuller87b11282017-06-23 16:43:45 +0100524 }
525 }
526 return;
527 }
528 }
529
530 pw.println("RulesManagerService state: " + toString());
Neil Fullerc9036142017-10-04 17:04:38 +0100531 pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
532 + ICU.getTZDataVersion() + ","
533 + ZoneInfoDB.getInstance().getVersion() + ","
534 + TimeZoneFinder.getInstance().getIanaVersion());
Neil Fullerd857f672017-07-20 11:00:35 +0100535 pw.println("Distro state: " + rulesState.toString());
Neil Fuller87b11282017-06-23 16:43:45 +0100536 mPackageTracker.dump(pw);
537 }
538
Neil Fullercd1a1092017-09-13 21:59:59 +0100539 /**
540 * Called when the device is considered idle.
541 */
542 void notifyIdle() {
543 // No package has changed: we are just triggering because the device is idle and there
544 // *might* be work to do.
545 final boolean packageChanged = false;
546 mPackageTracker.triggerUpdateIfNeeded(packageChanged);
547 }
548
Neil Fuller87b11282017-06-23 16:43:45 +0100549 @Override
550 public String toString() {
551 return "RulesManagerService{" +
552 "mOperationInProgress=" + mOperationInProgress +
553 '}';
554 }
555
Neil Fuller68f66662017-03-16 18:32:21 +0000556 private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) {
557 CheckToken checkToken;
558 try {
559 checkToken = CheckToken.fromByteArray(checkTokenBytes);
560 } catch (IOException e) {
561 throw new IllegalArgumentException("Unable to read token bytes "
562 + Arrays.toString(checkTokenBytes), e);
563 }
564 return checkToken;
565 }
Neil Fuller87b11282017-06-23 16:43:45 +0100566
567 private static String distroStatusToString(int distroStatus) {
568 switch(distroStatus) {
569 case DISTRO_STATUS_NONE:
570 return "None";
571 case DISTRO_STATUS_INSTALLED:
572 return "Installed";
573 case DISTRO_STATUS_UNKNOWN:
574 default:
575 return "Unknown";
576 }
577 }
578
579 private static String stagedOperationToString(int stagedOperationType) {
580 switch(stagedOperationType) {
581 case STAGED_OPERATION_NONE:
582 return "None";
583 case STAGED_OPERATION_UNINSTALL:
584 return "Uninstall";
585 case STAGED_OPERATION_INSTALL:
586 return "Install";
587 case STAGED_OPERATION_UNKNOWN:
588 default:
589 return "Unknown";
590 }
591 }
Neil Fullerd857f672017-07-20 11:00:35 +0100592
593 private static String toStringOrNull(Object obj) {
594 return obj == null ? null : obj.toString();
595 }
Neil Fuller68f66662017-03-16 18:32:21 +0000596}