blob: 30fc63c2a1b0bd2819100f18f208d4e1478fdabb [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() {
Neil Fuller35822592017-12-11 14:39:03 +0000124 // Return value deliberately ignored: no action required on failure to start.
Neil Fuller68f66662017-03-16 18:32:21 +0000125 mPackageTracker.start();
126 }
127
128 @Override // Binder call
129 public RulesState getRulesState() {
130 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
131
Neil Fuller87b11282017-06-23 16:43:45 +0100132 return getRulesStateInternal();
133 }
134
135 /** Like {@link #getRulesState()} without the permission check. */
136 private RulesState getRulesStateInternal() {
Neil Fuller68f66662017-03-16 18:32:21 +0000137 synchronized(this) {
138 String systemRulesVersion;
139 try {
140 systemRulesVersion = mInstaller.getSystemRulesVersion();
141 } catch (IOException e) {
142 Slog.w(TAG, "Failed to read system rules", e);
143 return null;
144 }
145
146 boolean operationInProgress = this.mOperationInProgress.get();
147
148 // Determine the staged operation status, if possible.
149 DistroRulesVersion stagedDistroRulesVersion = null;
Neil Fuller87b11282017-06-23 16:43:45 +0100150 int stagedOperationStatus = STAGED_OPERATION_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +0000151 if (!operationInProgress) {
152 StagedDistroOperation stagedDistroOperation;
153 try {
154 stagedDistroOperation = mInstaller.getStagedDistroOperation();
155 if (stagedDistroOperation == null) {
Neil Fuller87b11282017-06-23 16:43:45 +0100156 stagedOperationStatus = STAGED_OPERATION_NONE;
Neil Fuller68f66662017-03-16 18:32:21 +0000157 } else if (stagedDistroOperation.isUninstall) {
Neil Fuller87b11282017-06-23 16:43:45 +0100158 stagedOperationStatus = STAGED_OPERATION_UNINSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000159 } else {
160 // Must be an install.
Neil Fuller87b11282017-06-23 16:43:45 +0100161 stagedOperationStatus = STAGED_OPERATION_INSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000162 DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion;
163 stagedDistroRulesVersion = new DistroRulesVersion(
164 stagedDistroVersion.rulesVersion,
165 stagedDistroVersion.revision);
166 }
167 } catch (DistroException | IOException e) {
168 Slog.w(TAG, "Failed to read staged distro.", e);
169 }
170 }
171
172 // Determine the installed distro state, if possible.
173 DistroVersion installedDistroVersion;
Neil Fuller87b11282017-06-23 16:43:45 +0100174 int distroStatus = DISTRO_STATUS_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +0000175 DistroRulesVersion installedDistroRulesVersion = null;
176 if (!operationInProgress) {
177 try {
178 installedDistroVersion = mInstaller.getInstalledDistroVersion();
179 if (installedDistroVersion == null) {
Neil Fuller87b11282017-06-23 16:43:45 +0100180 distroStatus = DISTRO_STATUS_NONE;
Neil Fuller68f66662017-03-16 18:32:21 +0000181 installedDistroRulesVersion = null;
182 } else {
Neil Fuller87b11282017-06-23 16:43:45 +0100183 distroStatus = DISTRO_STATUS_INSTALLED;
Neil Fuller68f66662017-03-16 18:32:21 +0000184 installedDistroRulesVersion = new DistroRulesVersion(
185 installedDistroVersion.rulesVersion,
186 installedDistroVersion.revision);
187 }
188 } catch (DistroException | IOException e) {
189 Slog.w(TAG, "Failed to read installed distro.", e);
190 }
191 }
192 return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
193 operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
194 distroStatus, installedDistroRulesVersion);
195 }
196 }
197
198 @Override
Neil Fuller54525bf2017-06-22 14:10:29 +0100199 public int requestInstall(ParcelFileDescriptor distroParcelFileDescriptor,
200 byte[] checkTokenBytes, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000201
Neil Fuller54525bf2017-06-22 14:10:29 +0100202 boolean closeParcelFileDescriptorOnExit = true;
203 try {
204 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
Neil Fuller68f66662017-03-16 18:32:21 +0000205
Neil Fuller54525bf2017-06-22 14:10:29 +0100206 CheckToken checkToken = null;
207 if (checkTokenBytes != null) {
208 checkToken = createCheckTokenOrThrow(checkTokenBytes);
209 }
Neil Fullerd857f672017-07-20 11:00:35 +0100210 EventLogTags.writeTimezoneRequestInstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000211
Neil Fuller54525bf2017-06-22 14:10:29 +0100212 synchronized (this) {
213 if (distroParcelFileDescriptor == null) {
214 throw new NullPointerException("distroParcelFileDescriptor == null");
215 }
216 if (callback == null) {
217 throw new NullPointerException("observer == null");
218 }
219 if (mOperationInProgress.get()) {
220 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
221 }
222 mOperationInProgress.set(true);
223
224 // Execute the install asynchronously.
225 mExecutor.execute(
226 new InstallRunnable(distroParcelFileDescriptor, checkToken, callback));
227
228 // The InstallRunnable now owns the ParcelFileDescriptor, so it will close it after
229 // it executes (and we do not have to).
230 closeParcelFileDescriptorOnExit = false;
231
232 return RulesManager.SUCCESS;
233 }
234 } finally {
235 // We should close() the local ParcelFileDescriptor we were passed if it hasn't been
236 // passed to another thread to handle.
237 if (distroParcelFileDescriptor != null && closeParcelFileDescriptorOnExit) {
238 try {
239 distroParcelFileDescriptor.close();
240 } catch (IOException e) {
241 Slog.w(TAG, "Failed to close distroParcelFileDescriptor", e);
242 }
243 }
Neil Fuller68f66662017-03-16 18:32:21 +0000244 }
245 }
246
247 private class InstallRunnable implements Runnable {
248
Neil Fuller54525bf2017-06-22 14:10:29 +0100249 private final ParcelFileDescriptor mDistroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000250 private final CheckToken mCheckToken;
251 private final ICallback mCallback;
252
Neil Fuller54525bf2017-06-22 14:10:29 +0100253 InstallRunnable(ParcelFileDescriptor distroParcelFileDescriptor, CheckToken checkToken,
254 ICallback callback) {
255 mDistroParcelFileDescriptor = distroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000256 mCheckToken = checkToken;
257 mCallback = callback;
258 }
259
260 @Override
261 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100262 EventLogTags.writeTimezoneInstallStarted(toStringOrNull(mCheckToken));
263
Neil Fuller54525bf2017-06-22 14:10:29 +0100264 boolean success = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000265 // Adopt the ParcelFileDescriptor into this try-with-resources so it is closed
266 // when we are done.
Neil Fuller54525bf2017-06-22 14:10:29 +0100267 try (ParcelFileDescriptor pfd = mDistroParcelFileDescriptor) {
268 // The ParcelFileDescriptor owns the underlying FileDescriptor and we'll close
269 // it at the end of the try-with-resources.
270 final boolean isFdOwner = false;
271 InputStream is = new FileInputStream(pfd.getFileDescriptor(), isFdOwner);
272
273 TimeZoneDistro distro = new TimeZoneDistro(is);
Neil Fullerfe3b1182017-06-19 12:56:08 +0100274 int installerResult = mInstaller.stageInstallWithErrorCode(distro);
Neil Fuller68f66662017-03-16 18:32:21 +0000275 int resultCode = mapInstallerResultToApiCode(installerResult);
Neil Fullerd857f672017-07-20 11:00:35 +0100276 EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000277 sendFinishedStatus(mCallback, resultCode);
278
279 // All the installer failure modes are currently non-recoverable and won't be
280 // improved by trying again. Therefore success = true.
281 success = true;
282 } catch (Exception e) {
283 Slog.w(TAG, "Failed to install distro.", e);
Neil Fullerd857f672017-07-20 11:00:35 +0100284 EventLogTags.writeTimezoneInstallComplete(
285 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000286 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
287 } finally {
288 // Notify the package tracker that the operation is now complete.
289 mPackageTracker.recordCheckResult(mCheckToken, success);
290
291 mOperationInProgress.set(false);
292 }
293 }
294
295 private int mapInstallerResultToApiCode(int installerResult) {
296 switch (installerResult) {
297 case TimeZoneDistroInstaller.INSTALL_SUCCESS:
298 return Callback.SUCCESS;
299 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE:
300 return Callback.ERROR_INSTALL_BAD_DISTRO_STRUCTURE;
301 case TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD:
302 return Callback.ERROR_INSTALL_RULES_TOO_OLD;
303 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION:
304 return Callback.ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION;
305 case TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR:
306 return Callback.ERROR_INSTALL_VALIDATION_ERROR;
307 default:
308 return Callback.ERROR_UNKNOWN_FAILURE;
309 }
310 }
311 }
312
313 @Override
314 public int requestUninstall(byte[] checkTokenBytes, ICallback callback) {
315 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
316
317 CheckToken checkToken = null;
318 if (checkTokenBytes != null) {
319 checkToken = createCheckTokenOrThrow(checkTokenBytes);
320 }
Neil Fullerd857f672017-07-20 11:00:35 +0100321 EventLogTags.writeTimezoneRequestUninstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000322 synchronized(this) {
323 if (callback == null) {
324 throw new NullPointerException("callback == null");
325 }
326
327 if (mOperationInProgress.get()) {
328 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
329 }
330 mOperationInProgress.set(true);
331
332 // Execute the uninstall asynchronously.
333 mExecutor.execute(new UninstallRunnable(checkToken, callback));
334
335 return RulesManager.SUCCESS;
336 }
337 }
338
339 private class UninstallRunnable implements Runnable {
340
341 private final CheckToken mCheckToken;
342 private final ICallback mCallback;
343
Neil Fullera47c3632017-07-26 13:56:25 +0100344 UninstallRunnable(CheckToken checkToken, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000345 mCheckToken = checkToken;
346 mCallback = callback;
347 }
348
349 @Override
350 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100351 EventLogTags.writeTimezoneUninstallStarted(toStringOrNull(mCheckToken));
Neil Fuller8e27c922017-09-14 09:34:56 +0100352 boolean packageTrackerStatus = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000353 try {
Neil Fuller8e27c922017-09-14 09:34:56 +0100354 int uninstallResult = mInstaller.stageUninstall();
355 packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS
356 || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
357
358 // Right now we just have Callback.SUCCESS / Callback.ERROR_UNKNOWN_FAILURE for
359 // uninstall. All clients should be checking against SUCCESS. More granular failures
360 // may be added in future.
361 int callbackResultCode =
362 packageTrackerStatus ? Callback.SUCCESS : Callback.ERROR_UNKNOWN_FAILURE;
Neil Fullerd857f672017-07-20 11:00:35 +0100363 EventLogTags.writeTimezoneUninstallComplete(
Neil Fuller8e27c922017-09-14 09:34:56 +0100364 toStringOrNull(mCheckToken), callbackResultCode);
365 sendFinishedStatus(mCallback, callbackResultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000366 } catch (Exception e) {
Neil Fullerd857f672017-07-20 11:00:35 +0100367 EventLogTags.writeTimezoneUninstallComplete(
368 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000369 Slog.w(TAG, "Failed to uninstall distro.", e);
370 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
371 } finally {
372 // Notify the package tracker that the operation is now complete.
Neil Fuller8e27c922017-09-14 09:34:56 +0100373 mPackageTracker.recordCheckResult(mCheckToken, packageTrackerStatus);
Neil Fuller68f66662017-03-16 18:32:21 +0000374
375 mOperationInProgress.set(false);
376 }
377 }
378 }
379
380 private void sendFinishedStatus(ICallback callback, int resultCode) {
381 try {
382 callback.onFinished(resultCode);
383 } catch (RemoteException e) {
384 Slog.e(TAG, "Unable to notify observer of result", e);
385 }
386 }
387
388 @Override
389 public void requestNothing(byte[] checkTokenBytes, boolean success) {
390 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
391 CheckToken checkToken = null;
392 if (checkTokenBytes != null) {
393 checkToken = createCheckTokenOrThrow(checkTokenBytes);
394 }
Neil Fullerd857f672017-07-20 11:00:35 +0100395 EventLogTags.writeTimezoneRequestNothing(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000396 mPackageTracker.recordCheckResult(checkToken, success);
Neil Fullerd857f672017-07-20 11:00:35 +0100397 EventLogTags.writeTimezoneNothingComplete(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000398 }
399
Neil Fuller87b11282017-06-23 16:43:45 +0100400 @Override
401 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
402 if (!mPermissionHelper.checkDumpPermission(TAG, pw)) {
403 return;
404 }
405
406 RulesState rulesState = getRulesStateInternal();
407 if (args != null && args.length == 2) {
408 // Formatting options used for automated tests. The format is less free-form than
409 // the -format options, which are intended to be easier to parse.
410 if ("-format_state".equals(args[0]) && args[1] != null) {
411 for (char c : args[1].toCharArray()) {
412 switch (c) {
Neil Fullera47c3632017-07-26 13:56:25 +0100413 case 'p': {
414 // Report operation in progress
415 String value = "Unknown";
416 if (rulesState != null) {
417 value = Boolean.toString(rulesState.isOperationInProgress());
Neil Fuller87b11282017-06-23 16:43:45 +0100418 }
Neil Fullera47c3632017-07-26 13:56:25 +0100419 pw.println("Operation in progress: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100420 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100421 }
422 case 's': {
423 // Report system image rules version
424 String value = "Unknown";
425 if (rulesState != null) {
426 value = rulesState.getSystemRulesVersion();
427 }
428 pw.println("System rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100429 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100430 }
431 case 'c': {
432 // Report current installation state
433 String value = "Unknown";
434 if (rulesState != null) {
435 value = distroStatusToString(rulesState.getDistroStatus());
436 }
437 pw.println("Current install state: " + value);
438 break;
439 }
440 case 'i': {
441 // Report currently installed version
442 String value = "Unknown";
443 if (rulesState != null) {
444 DistroRulesVersion installedRulesVersion =
445 rulesState.getInstalledDistroRulesVersion();
446 if (installedRulesVersion == null) {
447 value = "<None>";
448 } else {
449 value = installedRulesVersion.toDumpString();
450 }
451 }
452 pw.println("Installed rules version: " + value);
453 break;
454 }
455 case 'o': {
456 // Report staged operation type
457 String value = "Unknown";
458 if (rulesState != null) {
459 int stagedOperationType = rulesState.getStagedOperationType();
460 value = stagedOperationToString(stagedOperationType);
461 }
462 pw.println("Staged operation: " + value);
463 break;
464 }
465 case 't': {
Neil Fuller87b11282017-06-23 16:43:45 +0100466 // Report staged version (i.e. the one that will be installed next boot
467 // if the staged operation is an install).
Neil Fullera47c3632017-07-26 13:56:25 +0100468 String value = "Unknown";
469 if (rulesState != null) {
470 DistroRulesVersion stagedDistroRulesVersion =
471 rulesState.getStagedDistroRulesVersion();
472 if (stagedDistroRulesVersion == null) {
473 value = "<None>";
474 } else {
475 value = stagedDistroRulesVersion.toDumpString();
476 }
Neil Fuller87b11282017-06-23 16:43:45 +0100477 }
Neil Fullera47c3632017-07-26 13:56:25 +0100478 pw.println("Staged rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100479 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100480 }
481 case 'a': {
Neil Fuller87b11282017-06-23 16:43:45 +0100482 // Report the active rules version (i.e. the rules in use by the current
483 // process).
Neil Fullerc9036142017-10-04 17:04:38 +0100484 pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
Neil Fuller87b11282017-06-23 16:43:45 +0100485 + ICU.getTZDataVersion() + ","
Neil Fullerc9036142017-10-04 17:04:38 +0100486 + ZoneInfoDB.getInstance().getVersion() + ","
487 + TimeZoneFinder.getInstance().getIanaVersion());
Neil Fuller87b11282017-06-23 16:43:45 +0100488 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100489 }
490 default: {
Neil Fuller87b11282017-06-23 16:43:45 +0100491 pw.println("Unknown option: " + c);
Neil Fullera47c3632017-07-26 13:56:25 +0100492 }
Neil Fuller87b11282017-06-23 16:43:45 +0100493 }
494 }
495 return;
496 }
497 }
498
499 pw.println("RulesManagerService state: " + toString());
Neil Fullerc9036142017-10-04 17:04:38 +0100500 pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
501 + ICU.getTZDataVersion() + ","
502 + ZoneInfoDB.getInstance().getVersion() + ","
503 + TimeZoneFinder.getInstance().getIanaVersion());
Neil Fullerd857f672017-07-20 11:00:35 +0100504 pw.println("Distro state: " + rulesState.toString());
Neil Fuller87b11282017-06-23 16:43:45 +0100505 mPackageTracker.dump(pw);
506 }
507
Neil Fullercd1a1092017-09-13 21:59:59 +0100508 /**
509 * Called when the device is considered idle.
510 */
511 void notifyIdle() {
512 // No package has changed: we are just triggering because the device is idle and there
513 // *might* be work to do.
514 final boolean packageChanged = false;
515 mPackageTracker.triggerUpdateIfNeeded(packageChanged);
516 }
517
Neil Fuller87b11282017-06-23 16:43:45 +0100518 @Override
519 public String toString() {
520 return "RulesManagerService{" +
521 "mOperationInProgress=" + mOperationInProgress +
522 '}';
523 }
524
Neil Fuller68f66662017-03-16 18:32:21 +0000525 private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) {
526 CheckToken checkToken;
527 try {
528 checkToken = CheckToken.fromByteArray(checkTokenBytes);
529 } catch (IOException e) {
530 throw new IllegalArgumentException("Unable to read token bytes "
531 + Arrays.toString(checkTokenBytes), e);
532 }
533 return checkToken;
534 }
Neil Fuller87b11282017-06-23 16:43:45 +0100535
536 private static String distroStatusToString(int distroStatus) {
537 switch(distroStatus) {
538 case DISTRO_STATUS_NONE:
539 return "None";
540 case DISTRO_STATUS_INSTALLED:
541 return "Installed";
542 case DISTRO_STATUS_UNKNOWN:
543 default:
544 return "Unknown";
545 }
546 }
547
548 private static String stagedOperationToString(int stagedOperationType) {
549 switch(stagedOperationType) {
550 case STAGED_OPERATION_NONE:
551 return "None";
552 case STAGED_OPERATION_UNINSTALL:
553 return "Uninstall";
554 case STAGED_OPERATION_INSTALL:
555 return "Install";
556 case STAGED_OPERATION_UNKNOWN:
557 default:
558 return "Unknown";
559 }
560 }
Neil Fullerd857f672017-07-20 11:00:35 +0100561
562 private static String toStringOrNull(Object obj) {
563 return obj == null ? null : obj.toString();
564 }
Neil Fuller68f66662017-03-16 18:32:21 +0000565}