blob: 52b49baf10737a96033309dd22503ffea9626b06 [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;
102 private final TimeZoneDistroInstaller mInstaller;
Neil Fuller68f66662017-03-16 18:32:21 +0000103
104 private static RulesManagerService create(Context context) {
105 RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context);
106 return new RulesManagerService(
107 helper /* permissionHelper */,
108 helper /* executor */,
Neil Fuller68f66662017-03-16 18:32:21 +0000109 PackageTracker.create(context),
110 new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR));
111 }
112
113 // A constructor that can be used by tests to supply mocked / faked dependencies.
114 RulesManagerService(PermissionHelper permissionHelper,
Neil Fuller54525bf2017-06-22 14:10:29 +0100115 Executor executor, PackageTracker packageTracker,
Neil Fuller68f66662017-03-16 18:32:21 +0000116 TimeZoneDistroInstaller timeZoneDistroInstaller) {
117 mPermissionHelper = permissionHelper;
118 mExecutor = executor;
Neil Fuller68f66662017-03-16 18:32:21 +0000119 mPackageTracker = packageTracker;
120 mInstaller = timeZoneDistroInstaller;
121 }
122
123 public void start() {
124 mPackageTracker.start();
125 }
126
127 @Override // Binder call
128 public RulesState getRulesState() {
129 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
130
Neil Fuller87b11282017-06-23 16:43:45 +0100131 return getRulesStateInternal();
132 }
133
134 /** Like {@link #getRulesState()} without the permission check. */
135 private RulesState getRulesStateInternal() {
Neil Fuller68f66662017-03-16 18:32:21 +0000136 synchronized(this) {
137 String systemRulesVersion;
138 try {
139 systemRulesVersion = mInstaller.getSystemRulesVersion();
140 } catch (IOException e) {
141 Slog.w(TAG, "Failed to read system rules", e);
142 return null;
143 }
144
145 boolean operationInProgress = this.mOperationInProgress.get();
146
147 // Determine the staged operation status, if possible.
148 DistroRulesVersion stagedDistroRulesVersion = null;
Neil Fuller87b11282017-06-23 16:43:45 +0100149 int stagedOperationStatus = STAGED_OPERATION_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +0000150 if (!operationInProgress) {
151 StagedDistroOperation stagedDistroOperation;
152 try {
153 stagedDistroOperation = mInstaller.getStagedDistroOperation();
154 if (stagedDistroOperation == null) {
Neil Fuller87b11282017-06-23 16:43:45 +0100155 stagedOperationStatus = STAGED_OPERATION_NONE;
Neil Fuller68f66662017-03-16 18:32:21 +0000156 } else if (stagedDistroOperation.isUninstall) {
Neil Fuller87b11282017-06-23 16:43:45 +0100157 stagedOperationStatus = STAGED_OPERATION_UNINSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000158 } else {
159 // Must be an install.
Neil Fuller87b11282017-06-23 16:43:45 +0100160 stagedOperationStatus = STAGED_OPERATION_INSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000161 DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion;
162 stagedDistroRulesVersion = new DistroRulesVersion(
163 stagedDistroVersion.rulesVersion,
164 stagedDistroVersion.revision);
165 }
166 } catch (DistroException | IOException e) {
167 Slog.w(TAG, "Failed to read staged distro.", e);
168 }
169 }
170
171 // Determine the installed distro state, if possible.
172 DistroVersion installedDistroVersion;
Neil Fuller87b11282017-06-23 16:43:45 +0100173 int distroStatus = DISTRO_STATUS_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +0000174 DistroRulesVersion installedDistroRulesVersion = null;
175 if (!operationInProgress) {
176 try {
177 installedDistroVersion = mInstaller.getInstalledDistroVersion();
178 if (installedDistroVersion == null) {
Neil Fuller87b11282017-06-23 16:43:45 +0100179 distroStatus = DISTRO_STATUS_NONE;
Neil Fuller68f66662017-03-16 18:32:21 +0000180 installedDistroRulesVersion = null;
181 } else {
Neil Fuller87b11282017-06-23 16:43:45 +0100182 distroStatus = DISTRO_STATUS_INSTALLED;
Neil Fuller68f66662017-03-16 18:32:21 +0000183 installedDistroRulesVersion = new DistroRulesVersion(
184 installedDistroVersion.rulesVersion,
185 installedDistroVersion.revision);
186 }
187 } catch (DistroException | IOException e) {
188 Slog.w(TAG, "Failed to read installed distro.", e);
189 }
190 }
191 return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
192 operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
193 distroStatus, installedDistroRulesVersion);
194 }
195 }
196
197 @Override
Neil Fuller54525bf2017-06-22 14:10:29 +0100198 public int requestInstall(ParcelFileDescriptor distroParcelFileDescriptor,
199 byte[] checkTokenBytes, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000200
Neil Fuller54525bf2017-06-22 14:10:29 +0100201 boolean closeParcelFileDescriptorOnExit = true;
202 try {
203 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
Neil Fuller68f66662017-03-16 18:32:21 +0000204
Neil Fuller54525bf2017-06-22 14:10:29 +0100205 CheckToken checkToken = null;
206 if (checkTokenBytes != null) {
207 checkToken = createCheckTokenOrThrow(checkTokenBytes);
208 }
Neil Fullerd857f672017-07-20 11:00:35 +0100209 EventLogTags.writeTimezoneRequestInstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000210
Neil Fuller54525bf2017-06-22 14:10:29 +0100211 synchronized (this) {
212 if (distroParcelFileDescriptor == null) {
213 throw new NullPointerException("distroParcelFileDescriptor == null");
214 }
215 if (callback == null) {
216 throw new NullPointerException("observer == null");
217 }
218 if (mOperationInProgress.get()) {
219 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
220 }
221 mOperationInProgress.set(true);
222
223 // Execute the install asynchronously.
224 mExecutor.execute(
225 new InstallRunnable(distroParcelFileDescriptor, checkToken, callback));
226
227 // The InstallRunnable now owns the ParcelFileDescriptor, so it will close it after
228 // it executes (and we do not have to).
229 closeParcelFileDescriptorOnExit = false;
230
231 return RulesManager.SUCCESS;
232 }
233 } finally {
234 // We should close() the local ParcelFileDescriptor we were passed if it hasn't been
235 // passed to another thread to handle.
236 if (distroParcelFileDescriptor != null && closeParcelFileDescriptorOnExit) {
237 try {
238 distroParcelFileDescriptor.close();
239 } catch (IOException e) {
240 Slog.w(TAG, "Failed to close distroParcelFileDescriptor", e);
241 }
242 }
Neil Fuller68f66662017-03-16 18:32:21 +0000243 }
244 }
245
246 private class InstallRunnable implements Runnable {
247
Neil Fuller54525bf2017-06-22 14:10:29 +0100248 private final ParcelFileDescriptor mDistroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000249 private final CheckToken mCheckToken;
250 private final ICallback mCallback;
251
Neil Fuller54525bf2017-06-22 14:10:29 +0100252 InstallRunnable(ParcelFileDescriptor distroParcelFileDescriptor, CheckToken checkToken,
253 ICallback callback) {
254 mDistroParcelFileDescriptor = distroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000255 mCheckToken = checkToken;
256 mCallback = callback;
257 }
258
259 @Override
260 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100261 EventLogTags.writeTimezoneInstallStarted(toStringOrNull(mCheckToken));
262
Neil Fuller54525bf2017-06-22 14:10:29 +0100263 boolean success = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000264 // Adopt the ParcelFileDescriptor into this try-with-resources so it is closed
265 // when we are done.
Neil Fuller54525bf2017-06-22 14:10:29 +0100266 try (ParcelFileDescriptor pfd = mDistroParcelFileDescriptor) {
267 // The ParcelFileDescriptor owns the underlying FileDescriptor and we'll close
268 // it at the end of the try-with-resources.
269 final boolean isFdOwner = false;
270 InputStream is = new FileInputStream(pfd.getFileDescriptor(), isFdOwner);
271
272 TimeZoneDistro distro = new TimeZoneDistro(is);
Neil Fullerfe3b1182017-06-19 12:56:08 +0100273 int installerResult = mInstaller.stageInstallWithErrorCode(distro);
Neil Fuller68f66662017-03-16 18:32:21 +0000274 int resultCode = mapInstallerResultToApiCode(installerResult);
Neil Fullerd857f672017-07-20 11:00:35 +0100275 EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000276 sendFinishedStatus(mCallback, resultCode);
277
278 // All the installer failure modes are currently non-recoverable and won't be
279 // improved by trying again. Therefore success = true.
280 success = true;
281 } catch (Exception e) {
282 Slog.w(TAG, "Failed to install distro.", e);
Neil Fullerd857f672017-07-20 11:00:35 +0100283 EventLogTags.writeTimezoneInstallComplete(
284 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000285 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
286 } finally {
287 // Notify the package tracker that the operation is now complete.
288 mPackageTracker.recordCheckResult(mCheckToken, success);
289
290 mOperationInProgress.set(false);
291 }
292 }
293
294 private int mapInstallerResultToApiCode(int installerResult) {
295 switch (installerResult) {
296 case TimeZoneDistroInstaller.INSTALL_SUCCESS:
297 return Callback.SUCCESS;
298 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE:
299 return Callback.ERROR_INSTALL_BAD_DISTRO_STRUCTURE;
300 case TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD:
301 return Callback.ERROR_INSTALL_RULES_TOO_OLD;
302 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION:
303 return Callback.ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION;
304 case TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR:
305 return Callback.ERROR_INSTALL_VALIDATION_ERROR;
306 default:
307 return Callback.ERROR_UNKNOWN_FAILURE;
308 }
309 }
310 }
311
312 @Override
313 public int requestUninstall(byte[] checkTokenBytes, ICallback callback) {
314 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
315
316 CheckToken checkToken = null;
317 if (checkTokenBytes != null) {
318 checkToken = createCheckTokenOrThrow(checkTokenBytes);
319 }
Neil Fullerd857f672017-07-20 11:00:35 +0100320 EventLogTags.writeTimezoneRequestUninstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000321 synchronized(this) {
322 if (callback == null) {
323 throw new NullPointerException("callback == null");
324 }
325
326 if (mOperationInProgress.get()) {
327 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
328 }
329 mOperationInProgress.set(true);
330
331 // Execute the uninstall asynchronously.
332 mExecutor.execute(new UninstallRunnable(checkToken, callback));
333
334 return RulesManager.SUCCESS;
335 }
336 }
337
338 private class UninstallRunnable implements Runnable {
339
340 private final CheckToken mCheckToken;
341 private final ICallback mCallback;
342
Neil Fullera47c3632017-07-26 13:56:25 +0100343 UninstallRunnable(CheckToken checkToken, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000344 mCheckToken = checkToken;
345 mCallback = callback;
346 }
347
348 @Override
349 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100350 EventLogTags.writeTimezoneUninstallStarted(toStringOrNull(mCheckToken));
Neil Fuller8e27c922017-09-14 09:34:56 +0100351 boolean packageTrackerStatus = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000352 try {
Neil Fuller8e27c922017-09-14 09:34:56 +0100353 int uninstallResult = mInstaller.stageUninstall();
354 packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS
355 || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
356
357 // Right now we just have Callback.SUCCESS / Callback.ERROR_UNKNOWN_FAILURE for
358 // uninstall. All clients should be checking against SUCCESS. More granular failures
359 // may be added in future.
360 int callbackResultCode =
361 packageTrackerStatus ? Callback.SUCCESS : Callback.ERROR_UNKNOWN_FAILURE;
Neil Fullerd857f672017-07-20 11:00:35 +0100362 EventLogTags.writeTimezoneUninstallComplete(
Neil Fuller8e27c922017-09-14 09:34:56 +0100363 toStringOrNull(mCheckToken), callbackResultCode);
364 sendFinishedStatus(mCallback, callbackResultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000365 } catch (Exception e) {
Neil Fullerd857f672017-07-20 11:00:35 +0100366 EventLogTags.writeTimezoneUninstallComplete(
367 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000368 Slog.w(TAG, "Failed to uninstall distro.", e);
369 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
370 } finally {
371 // Notify the package tracker that the operation is now complete.
Neil Fuller8e27c922017-09-14 09:34:56 +0100372 mPackageTracker.recordCheckResult(mCheckToken, packageTrackerStatus);
Neil Fuller68f66662017-03-16 18:32:21 +0000373
374 mOperationInProgress.set(false);
375 }
376 }
377 }
378
379 private void sendFinishedStatus(ICallback callback, int resultCode) {
380 try {
381 callback.onFinished(resultCode);
382 } catch (RemoteException e) {
383 Slog.e(TAG, "Unable to notify observer of result", e);
384 }
385 }
386
387 @Override
388 public void requestNothing(byte[] checkTokenBytes, boolean success) {
389 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
390 CheckToken checkToken = null;
391 if (checkTokenBytes != null) {
392 checkToken = createCheckTokenOrThrow(checkTokenBytes);
393 }
Neil Fullerd857f672017-07-20 11:00:35 +0100394 EventLogTags.writeTimezoneRequestNothing(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000395 mPackageTracker.recordCheckResult(checkToken, success);
Neil Fullerd857f672017-07-20 11:00:35 +0100396 EventLogTags.writeTimezoneNothingComplete(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000397 }
398
Neil Fuller87b11282017-06-23 16:43:45 +0100399 @Override
400 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
401 if (!mPermissionHelper.checkDumpPermission(TAG, pw)) {
402 return;
403 }
404
405 RulesState rulesState = getRulesStateInternal();
406 if (args != null && args.length == 2) {
407 // Formatting options used for automated tests. The format is less free-form than
408 // the -format options, which are intended to be easier to parse.
409 if ("-format_state".equals(args[0]) && args[1] != null) {
410 for (char c : args[1].toCharArray()) {
411 switch (c) {
Neil Fullera47c3632017-07-26 13:56:25 +0100412 case 'p': {
413 // Report operation in progress
414 String value = "Unknown";
415 if (rulesState != null) {
416 value = Boolean.toString(rulesState.isOperationInProgress());
Neil Fuller87b11282017-06-23 16:43:45 +0100417 }
Neil Fullera47c3632017-07-26 13:56:25 +0100418 pw.println("Operation in progress: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100419 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100420 }
421 case 's': {
422 // Report system image rules version
423 String value = "Unknown";
424 if (rulesState != null) {
425 value = rulesState.getSystemRulesVersion();
426 }
427 pw.println("System rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100428 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100429 }
430 case 'c': {
431 // Report current installation state
432 String value = "Unknown";
433 if (rulesState != null) {
434 value = distroStatusToString(rulesState.getDistroStatus());
435 }
436 pw.println("Current install state: " + value);
437 break;
438 }
439 case 'i': {
440 // Report currently installed version
441 String value = "Unknown";
442 if (rulesState != null) {
443 DistroRulesVersion installedRulesVersion =
444 rulesState.getInstalledDistroRulesVersion();
445 if (installedRulesVersion == null) {
446 value = "<None>";
447 } else {
448 value = installedRulesVersion.toDumpString();
449 }
450 }
451 pw.println("Installed rules version: " + value);
452 break;
453 }
454 case 'o': {
455 // Report staged operation type
456 String value = "Unknown";
457 if (rulesState != null) {
458 int stagedOperationType = rulesState.getStagedOperationType();
459 value = stagedOperationToString(stagedOperationType);
460 }
461 pw.println("Staged operation: " + value);
462 break;
463 }
464 case 't': {
Neil Fuller87b11282017-06-23 16:43:45 +0100465 // Report staged version (i.e. the one that will be installed next boot
466 // if the staged operation is an install).
Neil Fullera47c3632017-07-26 13:56:25 +0100467 String value = "Unknown";
468 if (rulesState != null) {
469 DistroRulesVersion stagedDistroRulesVersion =
470 rulesState.getStagedDistroRulesVersion();
471 if (stagedDistroRulesVersion == null) {
472 value = "<None>";
473 } else {
474 value = stagedDistroRulesVersion.toDumpString();
475 }
Neil Fuller87b11282017-06-23 16:43:45 +0100476 }
Neil Fullera47c3632017-07-26 13:56:25 +0100477 pw.println("Staged rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100478 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100479 }
480 case 'a': {
Neil Fuller87b11282017-06-23 16:43:45 +0100481 // Report the active rules version (i.e. the rules in use by the current
482 // process).
Neil Fullerc9036142017-10-04 17:04:38 +0100483 pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
Neil Fuller87b11282017-06-23 16:43:45 +0100484 + ICU.getTZDataVersion() + ","
Neil Fullerc9036142017-10-04 17:04:38 +0100485 + ZoneInfoDB.getInstance().getVersion() + ","
486 + TimeZoneFinder.getInstance().getIanaVersion());
Neil Fuller87b11282017-06-23 16:43:45 +0100487 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100488 }
489 default: {
Neil Fuller87b11282017-06-23 16:43:45 +0100490 pw.println("Unknown option: " + c);
Neil Fullera47c3632017-07-26 13:56:25 +0100491 }
Neil Fuller87b11282017-06-23 16:43:45 +0100492 }
493 }
494 return;
495 }
496 }
497
498 pw.println("RulesManagerService state: " + toString());
Neil Fullerc9036142017-10-04 17:04:38 +0100499 pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
500 + ICU.getTZDataVersion() + ","
501 + ZoneInfoDB.getInstance().getVersion() + ","
502 + TimeZoneFinder.getInstance().getIanaVersion());
Neil Fullerd857f672017-07-20 11:00:35 +0100503 pw.println("Distro state: " + rulesState.toString());
Neil Fuller87b11282017-06-23 16:43:45 +0100504 mPackageTracker.dump(pw);
505 }
506
Neil Fullercd1a1092017-09-13 21:59:59 +0100507 /**
508 * Called when the device is considered idle.
509 */
510 void notifyIdle() {
511 // No package has changed: we are just triggering because the device is idle and there
512 // *might* be work to do.
513 final boolean packageChanged = false;
514 mPackageTracker.triggerUpdateIfNeeded(packageChanged);
515 }
516
Neil Fuller87b11282017-06-23 16:43:45 +0100517 @Override
518 public String toString() {
519 return "RulesManagerService{" +
520 "mOperationInProgress=" + mOperationInProgress +
521 '}';
522 }
523
Neil Fuller68f66662017-03-16 18:32:21 +0000524 private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) {
525 CheckToken checkToken;
526 try {
527 checkToken = CheckToken.fromByteArray(checkTokenBytes);
528 } catch (IOException e) {
529 throw new IllegalArgumentException("Unable to read token bytes "
530 + Arrays.toString(checkTokenBytes), e);
531 }
532 return checkToken;
533 }
Neil Fuller87b11282017-06-23 16:43:45 +0100534
535 private static String distroStatusToString(int distroStatus) {
536 switch(distroStatus) {
537 case DISTRO_STATUS_NONE:
538 return "None";
539 case DISTRO_STATUS_INSTALLED:
540 return "Installed";
541 case DISTRO_STATUS_UNKNOWN:
542 default:
543 return "Unknown";
544 }
545 }
546
547 private static String stagedOperationToString(int stagedOperationType) {
548 switch(stagedOperationType) {
549 case STAGED_OPERATION_NONE:
550 return "None";
551 case STAGED_OPERATION_UNINSTALL:
552 return "Uninstall";
553 case STAGED_OPERATION_INSTALL:
554 return "Install";
555 case STAGED_OPERATION_UNKNOWN:
556 default:
557 return "Unknown";
558 }
559 }
Neil Fullerd857f672017-07-20 11:00:35 +0100560
561 private static String toStringOrNull(Object obj) {
562 return obj == null ? null : obj.toString();
563 }
Neil Fuller68f66662017-03-16 18:32:21 +0000564}