blob: 3ad4419c72739fde914911ab3f9f6cf99b6edf44 [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;
50import libcore.util.ZoneInfoDB;
51
52import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
53import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
54import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN;
55import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL;
56import static android.app.timezone.RulesState.STAGED_OPERATION_NONE;
57import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL;
58import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +000059
Neil Fuller68f66662017-03-16 18:32:21 +000060public final class RulesManagerService extends IRulesManager.Stub {
61
62 private static final String TAG = "timezone.RulesManagerService";
63
64 /** The distro format supported by this device. */
65 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
66 static final DistroFormatVersion DISTRO_FORMAT_VERSION_SUPPORTED =
67 new DistroFormatVersion(
68 DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
69 DistroVersion.CURRENT_FORMAT_MINOR_VERSION);
70
71 public static class Lifecycle extends SystemService {
72 private RulesManagerService mService;
73
74 public Lifecycle(Context context) {
75 super(context);
76 }
77
78 @Override
79 public void onStart() {
80 mService = RulesManagerService.create(getContext());
81 mService.start();
82
83 publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, mService);
84 }
85 }
86
87 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
88 static final String REQUIRED_UPDATER_PERMISSION =
89 android.Manifest.permission.UPDATE_TIME_ZONE_RULES;
90 private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata");
91 private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
92
93 private final AtomicBoolean mOperationInProgress = new AtomicBoolean(false);
94 private final PermissionHelper mPermissionHelper;
95 private final PackageTracker mPackageTracker;
96 private final Executor mExecutor;
97 private final TimeZoneDistroInstaller mInstaller;
Neil Fuller68f66662017-03-16 18:32:21 +000098
99 private static RulesManagerService create(Context context) {
100 RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context);
101 return new RulesManagerService(
102 helper /* permissionHelper */,
103 helper /* executor */,
Neil Fuller68f66662017-03-16 18:32:21 +0000104 PackageTracker.create(context),
105 new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR));
106 }
107
108 // A constructor that can be used by tests to supply mocked / faked dependencies.
109 RulesManagerService(PermissionHelper permissionHelper,
Neil Fuller54525bf2017-06-22 14:10:29 +0100110 Executor executor, PackageTracker packageTracker,
Neil Fuller68f66662017-03-16 18:32:21 +0000111 TimeZoneDistroInstaller timeZoneDistroInstaller) {
112 mPermissionHelper = permissionHelper;
113 mExecutor = executor;
Neil Fuller68f66662017-03-16 18:32:21 +0000114 mPackageTracker = packageTracker;
115 mInstaller = timeZoneDistroInstaller;
116 }
117
118 public void start() {
119 mPackageTracker.start();
120 }
121
122 @Override // Binder call
123 public RulesState getRulesState() {
124 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
125
Neil Fuller87b11282017-06-23 16:43:45 +0100126 return getRulesStateInternal();
127 }
128
129 /** Like {@link #getRulesState()} without the permission check. */
130 private RulesState getRulesStateInternal() {
Neil Fuller68f66662017-03-16 18:32:21 +0000131 synchronized(this) {
132 String systemRulesVersion;
133 try {
134 systemRulesVersion = mInstaller.getSystemRulesVersion();
135 } catch (IOException e) {
136 Slog.w(TAG, "Failed to read system rules", e);
137 return null;
138 }
139
140 boolean operationInProgress = this.mOperationInProgress.get();
141
142 // Determine the staged operation status, if possible.
143 DistroRulesVersion stagedDistroRulesVersion = null;
Neil Fuller87b11282017-06-23 16:43:45 +0100144 int stagedOperationStatus = STAGED_OPERATION_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +0000145 if (!operationInProgress) {
146 StagedDistroOperation stagedDistroOperation;
147 try {
148 stagedDistroOperation = mInstaller.getStagedDistroOperation();
149 if (stagedDistroOperation == null) {
Neil Fuller87b11282017-06-23 16:43:45 +0100150 stagedOperationStatus = STAGED_OPERATION_NONE;
Neil Fuller68f66662017-03-16 18:32:21 +0000151 } else if (stagedDistroOperation.isUninstall) {
Neil Fuller87b11282017-06-23 16:43:45 +0100152 stagedOperationStatus = STAGED_OPERATION_UNINSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000153 } else {
154 // Must be an install.
Neil Fuller87b11282017-06-23 16:43:45 +0100155 stagedOperationStatus = STAGED_OPERATION_INSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000156 DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion;
157 stagedDistroRulesVersion = new DistroRulesVersion(
158 stagedDistroVersion.rulesVersion,
159 stagedDistroVersion.revision);
160 }
161 } catch (DistroException | IOException e) {
162 Slog.w(TAG, "Failed to read staged distro.", e);
163 }
164 }
165
166 // Determine the installed distro state, if possible.
167 DistroVersion installedDistroVersion;
Neil Fuller87b11282017-06-23 16:43:45 +0100168 int distroStatus = DISTRO_STATUS_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +0000169 DistroRulesVersion installedDistroRulesVersion = null;
170 if (!operationInProgress) {
171 try {
172 installedDistroVersion = mInstaller.getInstalledDistroVersion();
173 if (installedDistroVersion == null) {
Neil Fuller87b11282017-06-23 16:43:45 +0100174 distroStatus = DISTRO_STATUS_NONE;
Neil Fuller68f66662017-03-16 18:32:21 +0000175 installedDistroRulesVersion = null;
176 } else {
Neil Fuller87b11282017-06-23 16:43:45 +0100177 distroStatus = DISTRO_STATUS_INSTALLED;
Neil Fuller68f66662017-03-16 18:32:21 +0000178 installedDistroRulesVersion = new DistroRulesVersion(
179 installedDistroVersion.rulesVersion,
180 installedDistroVersion.revision);
181 }
182 } catch (DistroException | IOException e) {
183 Slog.w(TAG, "Failed to read installed distro.", e);
184 }
185 }
186 return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
187 operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
188 distroStatus, installedDistroRulesVersion);
189 }
190 }
191
192 @Override
Neil Fuller54525bf2017-06-22 14:10:29 +0100193 public int requestInstall(ParcelFileDescriptor distroParcelFileDescriptor,
194 byte[] checkTokenBytes, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000195
Neil Fuller54525bf2017-06-22 14:10:29 +0100196 boolean closeParcelFileDescriptorOnExit = true;
197 try {
198 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
Neil Fuller68f66662017-03-16 18:32:21 +0000199
Neil Fuller54525bf2017-06-22 14:10:29 +0100200 CheckToken checkToken = null;
201 if (checkTokenBytes != null) {
202 checkToken = createCheckTokenOrThrow(checkTokenBytes);
203 }
Neil Fullerd857f672017-07-20 11:00:35 +0100204 EventLogTags.writeTimezoneRequestInstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000205
Neil Fuller54525bf2017-06-22 14:10:29 +0100206 synchronized (this) {
207 if (distroParcelFileDescriptor == null) {
208 throw new NullPointerException("distroParcelFileDescriptor == null");
209 }
210 if (callback == null) {
211 throw new NullPointerException("observer == null");
212 }
213 if (mOperationInProgress.get()) {
214 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
215 }
216 mOperationInProgress.set(true);
217
218 // Execute the install asynchronously.
219 mExecutor.execute(
220 new InstallRunnable(distroParcelFileDescriptor, checkToken, callback));
221
222 // The InstallRunnable now owns the ParcelFileDescriptor, so it will close it after
223 // it executes (and we do not have to).
224 closeParcelFileDescriptorOnExit = false;
225
226 return RulesManager.SUCCESS;
227 }
228 } finally {
229 // We should close() the local ParcelFileDescriptor we were passed if it hasn't been
230 // passed to another thread to handle.
231 if (distroParcelFileDescriptor != null && closeParcelFileDescriptorOnExit) {
232 try {
233 distroParcelFileDescriptor.close();
234 } catch (IOException e) {
235 Slog.w(TAG, "Failed to close distroParcelFileDescriptor", e);
236 }
237 }
Neil Fuller68f66662017-03-16 18:32:21 +0000238 }
239 }
240
241 private class InstallRunnable implements Runnable {
242
Neil Fuller54525bf2017-06-22 14:10:29 +0100243 private final ParcelFileDescriptor mDistroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000244 private final CheckToken mCheckToken;
245 private final ICallback mCallback;
246
Neil Fuller54525bf2017-06-22 14:10:29 +0100247 InstallRunnable(ParcelFileDescriptor distroParcelFileDescriptor, CheckToken checkToken,
248 ICallback callback) {
249 mDistroParcelFileDescriptor = distroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000250 mCheckToken = checkToken;
251 mCallback = callback;
252 }
253
254 @Override
255 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100256 EventLogTags.writeTimezoneInstallStarted(toStringOrNull(mCheckToken));
257
Neil Fuller54525bf2017-06-22 14:10:29 +0100258 boolean success = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000259 // Adopt the ParcelFileDescriptor into this try-with-resources so it is closed
260 // when we are done.
Neil Fuller54525bf2017-06-22 14:10:29 +0100261 try (ParcelFileDescriptor pfd = mDistroParcelFileDescriptor) {
262 // The ParcelFileDescriptor owns the underlying FileDescriptor and we'll close
263 // it at the end of the try-with-resources.
264 final boolean isFdOwner = false;
265 InputStream is = new FileInputStream(pfd.getFileDescriptor(), isFdOwner);
266
267 TimeZoneDistro distro = new TimeZoneDistro(is);
Neil Fullerfe3b1182017-06-19 12:56:08 +0100268 int installerResult = mInstaller.stageInstallWithErrorCode(distro);
Neil Fuller68f66662017-03-16 18:32:21 +0000269 int resultCode = mapInstallerResultToApiCode(installerResult);
Neil Fullerd857f672017-07-20 11:00:35 +0100270 EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000271 sendFinishedStatus(mCallback, resultCode);
272
273 // All the installer failure modes are currently non-recoverable and won't be
274 // improved by trying again. Therefore success = true.
275 success = true;
276 } catch (Exception e) {
277 Slog.w(TAG, "Failed to install distro.", e);
Neil Fullerd857f672017-07-20 11:00:35 +0100278 EventLogTags.writeTimezoneInstallComplete(
279 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000280 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
281 } finally {
282 // Notify the package tracker that the operation is now complete.
283 mPackageTracker.recordCheckResult(mCheckToken, success);
284
285 mOperationInProgress.set(false);
286 }
287 }
288
289 private int mapInstallerResultToApiCode(int installerResult) {
290 switch (installerResult) {
291 case TimeZoneDistroInstaller.INSTALL_SUCCESS:
292 return Callback.SUCCESS;
293 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE:
294 return Callback.ERROR_INSTALL_BAD_DISTRO_STRUCTURE;
295 case TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD:
296 return Callback.ERROR_INSTALL_RULES_TOO_OLD;
297 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION:
298 return Callback.ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION;
299 case TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR:
300 return Callback.ERROR_INSTALL_VALIDATION_ERROR;
301 default:
302 return Callback.ERROR_UNKNOWN_FAILURE;
303 }
304 }
305 }
306
307 @Override
308 public int requestUninstall(byte[] checkTokenBytes, ICallback callback) {
309 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
310
311 CheckToken checkToken = null;
312 if (checkTokenBytes != null) {
313 checkToken = createCheckTokenOrThrow(checkTokenBytes);
314 }
Neil Fullerd857f672017-07-20 11:00:35 +0100315 EventLogTags.writeTimezoneRequestUninstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000316 synchronized(this) {
317 if (callback == null) {
318 throw new NullPointerException("callback == null");
319 }
320
321 if (mOperationInProgress.get()) {
322 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
323 }
324 mOperationInProgress.set(true);
325
326 // Execute the uninstall asynchronously.
327 mExecutor.execute(new UninstallRunnable(checkToken, callback));
328
329 return RulesManager.SUCCESS;
330 }
331 }
332
333 private class UninstallRunnable implements Runnable {
334
335 private final CheckToken mCheckToken;
336 private final ICallback mCallback;
337
Neil Fullera47c3632017-07-26 13:56:25 +0100338 UninstallRunnable(CheckToken checkToken, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000339 mCheckToken = checkToken;
340 mCallback = callback;
341 }
342
343 @Override
344 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100345 EventLogTags.writeTimezoneUninstallStarted(toStringOrNull(mCheckToken));
Neil Fuller8e27c922017-09-14 09:34:56 +0100346 boolean packageTrackerStatus = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000347 try {
Neil Fuller8e27c922017-09-14 09:34:56 +0100348 int uninstallResult = mInstaller.stageUninstall();
349 packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS
350 || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
351
352 // Right now we just have Callback.SUCCESS / Callback.ERROR_UNKNOWN_FAILURE for
353 // uninstall. All clients should be checking against SUCCESS. More granular failures
354 // may be added in future.
355 int callbackResultCode =
356 packageTrackerStatus ? Callback.SUCCESS : Callback.ERROR_UNKNOWN_FAILURE;
Neil Fullerd857f672017-07-20 11:00:35 +0100357 EventLogTags.writeTimezoneUninstallComplete(
Neil Fuller8e27c922017-09-14 09:34:56 +0100358 toStringOrNull(mCheckToken), callbackResultCode);
359 sendFinishedStatus(mCallback, callbackResultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000360 } catch (Exception e) {
Neil Fullerd857f672017-07-20 11:00:35 +0100361 EventLogTags.writeTimezoneUninstallComplete(
362 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000363 Slog.w(TAG, "Failed to uninstall distro.", e);
364 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
365 } finally {
366 // Notify the package tracker that the operation is now complete.
Neil Fuller8e27c922017-09-14 09:34:56 +0100367 mPackageTracker.recordCheckResult(mCheckToken, packageTrackerStatus);
Neil Fuller68f66662017-03-16 18:32:21 +0000368
369 mOperationInProgress.set(false);
370 }
371 }
372 }
373
374 private void sendFinishedStatus(ICallback callback, int resultCode) {
375 try {
376 callback.onFinished(resultCode);
377 } catch (RemoteException e) {
378 Slog.e(TAG, "Unable to notify observer of result", e);
379 }
380 }
381
382 @Override
383 public void requestNothing(byte[] checkTokenBytes, boolean success) {
384 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
385 CheckToken checkToken = null;
386 if (checkTokenBytes != null) {
387 checkToken = createCheckTokenOrThrow(checkTokenBytes);
388 }
Neil Fullerd857f672017-07-20 11:00:35 +0100389 EventLogTags.writeTimezoneRequestNothing(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000390 mPackageTracker.recordCheckResult(checkToken, success);
Neil Fullerd857f672017-07-20 11:00:35 +0100391 EventLogTags.writeTimezoneNothingComplete(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000392 }
393
Neil Fuller87b11282017-06-23 16:43:45 +0100394 @Override
395 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
396 if (!mPermissionHelper.checkDumpPermission(TAG, pw)) {
397 return;
398 }
399
400 RulesState rulesState = getRulesStateInternal();
401 if (args != null && args.length == 2) {
402 // Formatting options used for automated tests. The format is less free-form than
403 // the -format options, which are intended to be easier to parse.
404 if ("-format_state".equals(args[0]) && args[1] != null) {
405 for (char c : args[1].toCharArray()) {
406 switch (c) {
Neil Fullera47c3632017-07-26 13:56:25 +0100407 case 'p': {
408 // Report operation in progress
409 String value = "Unknown";
410 if (rulesState != null) {
411 value = Boolean.toString(rulesState.isOperationInProgress());
Neil Fuller87b11282017-06-23 16:43:45 +0100412 }
Neil Fullera47c3632017-07-26 13:56:25 +0100413 pw.println("Operation in progress: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100414 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100415 }
416 case 's': {
417 // Report system image rules version
418 String value = "Unknown";
419 if (rulesState != null) {
420 value = rulesState.getSystemRulesVersion();
421 }
422 pw.println("System rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100423 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100424 }
425 case 'c': {
426 // Report current installation state
427 String value = "Unknown";
428 if (rulesState != null) {
429 value = distroStatusToString(rulesState.getDistroStatus());
430 }
431 pw.println("Current install state: " + value);
432 break;
433 }
434 case 'i': {
435 // Report currently installed version
436 String value = "Unknown";
437 if (rulesState != null) {
438 DistroRulesVersion installedRulesVersion =
439 rulesState.getInstalledDistroRulesVersion();
440 if (installedRulesVersion == null) {
441 value = "<None>";
442 } else {
443 value = installedRulesVersion.toDumpString();
444 }
445 }
446 pw.println("Installed rules version: " + value);
447 break;
448 }
449 case 'o': {
450 // Report staged operation type
451 String value = "Unknown";
452 if (rulesState != null) {
453 int stagedOperationType = rulesState.getStagedOperationType();
454 value = stagedOperationToString(stagedOperationType);
455 }
456 pw.println("Staged operation: " + value);
457 break;
458 }
459 case 't': {
Neil Fuller87b11282017-06-23 16:43:45 +0100460 // Report staged version (i.e. the one that will be installed next boot
461 // if the staged operation is an install).
Neil Fullera47c3632017-07-26 13:56:25 +0100462 String value = "Unknown";
463 if (rulesState != null) {
464 DistroRulesVersion stagedDistroRulesVersion =
465 rulesState.getStagedDistroRulesVersion();
466 if (stagedDistroRulesVersion == null) {
467 value = "<None>";
468 } else {
469 value = stagedDistroRulesVersion.toDumpString();
470 }
Neil Fuller87b11282017-06-23 16:43:45 +0100471 }
Neil Fullera47c3632017-07-26 13:56:25 +0100472 pw.println("Staged rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100473 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100474 }
475 case 'a': {
Neil Fuller87b11282017-06-23 16:43:45 +0100476 // Report the active rules version (i.e. the rules in use by the current
477 // process).
478 pw.println("Active rules version (ICU, libcore): "
479 + ICU.getTZDataVersion() + ","
480 + ZoneInfoDB.getInstance().getVersion());
481 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100482 }
483 default: {
Neil Fuller87b11282017-06-23 16:43:45 +0100484 pw.println("Unknown option: " + c);
Neil Fullera47c3632017-07-26 13:56:25 +0100485 }
Neil Fuller87b11282017-06-23 16:43:45 +0100486 }
487 }
488 return;
489 }
490 }
491
492 pw.println("RulesManagerService state: " + toString());
493 pw.println("Active rules version (ICU, libcore): " + ICU.getTZDataVersion() + ","
494 + ZoneInfoDB.getInstance().getVersion());
Neil Fullerd857f672017-07-20 11:00:35 +0100495 pw.println("Distro state: " + rulesState.toString());
Neil Fuller87b11282017-06-23 16:43:45 +0100496 mPackageTracker.dump(pw);
497 }
498
499 @Override
500 public String toString() {
501 return "RulesManagerService{" +
502 "mOperationInProgress=" + mOperationInProgress +
503 '}';
504 }
505
Neil Fuller68f66662017-03-16 18:32:21 +0000506 private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) {
507 CheckToken checkToken;
508 try {
509 checkToken = CheckToken.fromByteArray(checkTokenBytes);
510 } catch (IOException e) {
511 throw new IllegalArgumentException("Unable to read token bytes "
512 + Arrays.toString(checkTokenBytes), e);
513 }
514 return checkToken;
515 }
Neil Fuller87b11282017-06-23 16:43:45 +0100516
517 private static String distroStatusToString(int distroStatus) {
518 switch(distroStatus) {
519 case DISTRO_STATUS_NONE:
520 return "None";
521 case DISTRO_STATUS_INSTALLED:
522 return "Installed";
523 case DISTRO_STATUS_UNKNOWN:
524 default:
525 return "Unknown";
526 }
527 }
528
529 private static String stagedOperationToString(int stagedOperationType) {
530 switch(stagedOperationType) {
531 case STAGED_OPERATION_NONE:
532 return "None";
533 case STAGED_OPERATION_UNINSTALL:
534 return "Uninstall";
535 case STAGED_OPERATION_INSTALL:
536 return "Install";
537 case STAGED_OPERATION_UNKNOWN:
538 default:
539 return "Unknown";
540 }
541 }
Neil Fullerd857f672017-07-20 11:00:35 +0100542
543 private static String toStringOrNull(Object obj) {
544 return obj == null ? null : obj.toString();
545 }
Neil Fuller68f66662017-03-16 18:32:21 +0000546}