blob: 6824a597a5f8ab4d1401a9d2198eccc32a28d113 [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 {
Neil Fuller68f66662017-03-16 18:32:21 +000072 public Lifecycle(Context context) {
73 super(context);
74 }
75
76 @Override
77 public void onStart() {
Neil Fullercd1a1092017-09-13 21:59:59 +010078 RulesManagerService service = RulesManagerService.create(getContext());
79 service.start();
Neil Fuller68f66662017-03-16 18:32:21 +000080
Neil Fullercd1a1092017-09-13 21:59:59 +010081 // Publish the binder service so it can be accessed from other (appropriately
82 // permissioned) processes.
83 publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, service);
84
85 // Publish the service instance locally so we can use it directly from within the system
86 // server from TimeZoneUpdateIdler.
87 publishLocalService(RulesManagerService.class, service);
Neil Fuller68f66662017-03-16 18:32:21 +000088 }
89 }
90
91 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
92 static final String REQUIRED_UPDATER_PERMISSION =
93 android.Manifest.permission.UPDATE_TIME_ZONE_RULES;
94 private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata");
95 private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
96
97 private final AtomicBoolean mOperationInProgress = new AtomicBoolean(false);
98 private final PermissionHelper mPermissionHelper;
99 private final PackageTracker mPackageTracker;
100 private final Executor mExecutor;
101 private final TimeZoneDistroInstaller mInstaller;
Neil Fuller68f66662017-03-16 18:32:21 +0000102
103 private static RulesManagerService create(Context context) {
104 RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context);
105 return new RulesManagerService(
106 helper /* permissionHelper */,
107 helper /* executor */,
Neil Fuller68f66662017-03-16 18:32:21 +0000108 PackageTracker.create(context),
109 new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR));
110 }
111
112 // A constructor that can be used by tests to supply mocked / faked dependencies.
113 RulesManagerService(PermissionHelper permissionHelper,
Neil Fuller54525bf2017-06-22 14:10:29 +0100114 Executor executor, PackageTracker packageTracker,
Neil Fuller68f66662017-03-16 18:32:21 +0000115 TimeZoneDistroInstaller timeZoneDistroInstaller) {
116 mPermissionHelper = permissionHelper;
117 mExecutor = executor;
Neil Fuller68f66662017-03-16 18:32:21 +0000118 mPackageTracker = packageTracker;
119 mInstaller = timeZoneDistroInstaller;
120 }
121
122 public void start() {
123 mPackageTracker.start();
124 }
125
126 @Override // Binder call
127 public RulesState getRulesState() {
128 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
129
Neil Fuller87b11282017-06-23 16:43:45 +0100130 return getRulesStateInternal();
131 }
132
133 /** Like {@link #getRulesState()} without the permission check. */
134 private RulesState getRulesStateInternal() {
Neil Fuller68f66662017-03-16 18:32:21 +0000135 synchronized(this) {
136 String systemRulesVersion;
137 try {
138 systemRulesVersion = mInstaller.getSystemRulesVersion();
139 } catch (IOException e) {
140 Slog.w(TAG, "Failed to read system rules", e);
141 return null;
142 }
143
144 boolean operationInProgress = this.mOperationInProgress.get();
145
146 // Determine the staged operation status, if possible.
147 DistroRulesVersion stagedDistroRulesVersion = null;
Neil Fuller87b11282017-06-23 16:43:45 +0100148 int stagedOperationStatus = STAGED_OPERATION_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +0000149 if (!operationInProgress) {
150 StagedDistroOperation stagedDistroOperation;
151 try {
152 stagedDistroOperation = mInstaller.getStagedDistroOperation();
153 if (stagedDistroOperation == null) {
Neil Fuller87b11282017-06-23 16:43:45 +0100154 stagedOperationStatus = STAGED_OPERATION_NONE;
Neil Fuller68f66662017-03-16 18:32:21 +0000155 } else if (stagedDistroOperation.isUninstall) {
Neil Fuller87b11282017-06-23 16:43:45 +0100156 stagedOperationStatus = STAGED_OPERATION_UNINSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000157 } else {
158 // Must be an install.
Neil Fuller87b11282017-06-23 16:43:45 +0100159 stagedOperationStatus = STAGED_OPERATION_INSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000160 DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion;
161 stagedDistroRulesVersion = new DistroRulesVersion(
162 stagedDistroVersion.rulesVersion,
163 stagedDistroVersion.revision);
164 }
165 } catch (DistroException | IOException e) {
166 Slog.w(TAG, "Failed to read staged distro.", e);
167 }
168 }
169
170 // Determine the installed distro state, if possible.
171 DistroVersion installedDistroVersion;
Neil Fuller87b11282017-06-23 16:43:45 +0100172 int distroStatus = DISTRO_STATUS_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +0000173 DistroRulesVersion installedDistroRulesVersion = null;
174 if (!operationInProgress) {
175 try {
176 installedDistroVersion = mInstaller.getInstalledDistroVersion();
177 if (installedDistroVersion == null) {
Neil Fuller87b11282017-06-23 16:43:45 +0100178 distroStatus = DISTRO_STATUS_NONE;
Neil Fuller68f66662017-03-16 18:32:21 +0000179 installedDistroRulesVersion = null;
180 } else {
Neil Fuller87b11282017-06-23 16:43:45 +0100181 distroStatus = DISTRO_STATUS_INSTALLED;
Neil Fuller68f66662017-03-16 18:32:21 +0000182 installedDistroRulesVersion = new DistroRulesVersion(
183 installedDistroVersion.rulesVersion,
184 installedDistroVersion.revision);
185 }
186 } catch (DistroException | IOException e) {
187 Slog.w(TAG, "Failed to read installed distro.", e);
188 }
189 }
190 return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
191 operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
192 distroStatus, installedDistroRulesVersion);
193 }
194 }
195
196 @Override
Neil Fuller54525bf2017-06-22 14:10:29 +0100197 public int requestInstall(ParcelFileDescriptor distroParcelFileDescriptor,
198 byte[] checkTokenBytes, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000199
Neil Fuller54525bf2017-06-22 14:10:29 +0100200 boolean closeParcelFileDescriptorOnExit = true;
201 try {
202 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
Neil Fuller68f66662017-03-16 18:32:21 +0000203
Neil Fuller54525bf2017-06-22 14:10:29 +0100204 CheckToken checkToken = null;
205 if (checkTokenBytes != null) {
206 checkToken = createCheckTokenOrThrow(checkTokenBytes);
207 }
Neil Fullerd857f672017-07-20 11:00:35 +0100208 EventLogTags.writeTimezoneRequestInstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000209
Neil Fuller54525bf2017-06-22 14:10:29 +0100210 synchronized (this) {
211 if (distroParcelFileDescriptor == null) {
212 throw new NullPointerException("distroParcelFileDescriptor == null");
213 }
214 if (callback == null) {
215 throw new NullPointerException("observer == null");
216 }
217 if (mOperationInProgress.get()) {
218 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
219 }
220 mOperationInProgress.set(true);
221
222 // Execute the install asynchronously.
223 mExecutor.execute(
224 new InstallRunnable(distroParcelFileDescriptor, checkToken, callback));
225
226 // The InstallRunnable now owns the ParcelFileDescriptor, so it will close it after
227 // it executes (and we do not have to).
228 closeParcelFileDescriptorOnExit = false;
229
230 return RulesManager.SUCCESS;
231 }
232 } finally {
233 // We should close() the local ParcelFileDescriptor we were passed if it hasn't been
234 // passed to another thread to handle.
235 if (distroParcelFileDescriptor != null && closeParcelFileDescriptorOnExit) {
236 try {
237 distroParcelFileDescriptor.close();
238 } catch (IOException e) {
239 Slog.w(TAG, "Failed to close distroParcelFileDescriptor", e);
240 }
241 }
Neil Fuller68f66662017-03-16 18:32:21 +0000242 }
243 }
244
245 private class InstallRunnable implements Runnable {
246
Neil Fuller54525bf2017-06-22 14:10:29 +0100247 private final ParcelFileDescriptor mDistroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000248 private final CheckToken mCheckToken;
249 private final ICallback mCallback;
250
Neil Fuller54525bf2017-06-22 14:10:29 +0100251 InstallRunnable(ParcelFileDescriptor distroParcelFileDescriptor, CheckToken checkToken,
252 ICallback callback) {
253 mDistroParcelFileDescriptor = distroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000254 mCheckToken = checkToken;
255 mCallback = callback;
256 }
257
258 @Override
259 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100260 EventLogTags.writeTimezoneInstallStarted(toStringOrNull(mCheckToken));
261
Neil Fuller54525bf2017-06-22 14:10:29 +0100262 boolean success = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000263 // Adopt the ParcelFileDescriptor into this try-with-resources so it is closed
264 // when we are done.
Neil Fuller54525bf2017-06-22 14:10:29 +0100265 try (ParcelFileDescriptor pfd = mDistroParcelFileDescriptor) {
266 // The ParcelFileDescriptor owns the underlying FileDescriptor and we'll close
267 // it at the end of the try-with-resources.
268 final boolean isFdOwner = false;
269 InputStream is = new FileInputStream(pfd.getFileDescriptor(), isFdOwner);
270
271 TimeZoneDistro distro = new TimeZoneDistro(is);
Neil Fullerfe3b1182017-06-19 12:56:08 +0100272 int installerResult = mInstaller.stageInstallWithErrorCode(distro);
Neil Fuller68f66662017-03-16 18:32:21 +0000273 int resultCode = mapInstallerResultToApiCode(installerResult);
Neil Fullerd857f672017-07-20 11:00:35 +0100274 EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000275 sendFinishedStatus(mCallback, resultCode);
276
277 // All the installer failure modes are currently non-recoverable and won't be
278 // improved by trying again. Therefore success = true.
279 success = true;
280 } catch (Exception e) {
281 Slog.w(TAG, "Failed to install distro.", e);
Neil Fullerd857f672017-07-20 11:00:35 +0100282 EventLogTags.writeTimezoneInstallComplete(
283 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000284 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
285 } finally {
286 // Notify the package tracker that the operation is now complete.
287 mPackageTracker.recordCheckResult(mCheckToken, success);
288
289 mOperationInProgress.set(false);
290 }
291 }
292
293 private int mapInstallerResultToApiCode(int installerResult) {
294 switch (installerResult) {
295 case TimeZoneDistroInstaller.INSTALL_SUCCESS:
296 return Callback.SUCCESS;
297 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE:
298 return Callback.ERROR_INSTALL_BAD_DISTRO_STRUCTURE;
299 case TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD:
300 return Callback.ERROR_INSTALL_RULES_TOO_OLD;
301 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION:
302 return Callback.ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION;
303 case TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR:
304 return Callback.ERROR_INSTALL_VALIDATION_ERROR;
305 default:
306 return Callback.ERROR_UNKNOWN_FAILURE;
307 }
308 }
309 }
310
311 @Override
312 public int requestUninstall(byte[] checkTokenBytes, ICallback callback) {
313 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
314
315 CheckToken checkToken = null;
316 if (checkTokenBytes != null) {
317 checkToken = createCheckTokenOrThrow(checkTokenBytes);
318 }
Neil Fullerd857f672017-07-20 11:00:35 +0100319 EventLogTags.writeTimezoneRequestUninstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000320 synchronized(this) {
321 if (callback == null) {
322 throw new NullPointerException("callback == null");
323 }
324
325 if (mOperationInProgress.get()) {
326 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
327 }
328 mOperationInProgress.set(true);
329
330 // Execute the uninstall asynchronously.
331 mExecutor.execute(new UninstallRunnable(checkToken, callback));
332
333 return RulesManager.SUCCESS;
334 }
335 }
336
337 private class UninstallRunnable implements Runnable {
338
339 private final CheckToken mCheckToken;
340 private final ICallback mCallback;
341
Neil Fullera47c3632017-07-26 13:56:25 +0100342 UninstallRunnable(CheckToken checkToken, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000343 mCheckToken = checkToken;
344 mCallback = callback;
345 }
346
347 @Override
348 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100349 EventLogTags.writeTimezoneUninstallStarted(toStringOrNull(mCheckToken));
Neil Fuller8e27c922017-09-14 09:34:56 +0100350 boolean packageTrackerStatus = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000351 try {
Neil Fuller8e27c922017-09-14 09:34:56 +0100352 int uninstallResult = mInstaller.stageUninstall();
353 packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS
354 || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
355
356 // Right now we just have Callback.SUCCESS / Callback.ERROR_UNKNOWN_FAILURE for
357 // uninstall. All clients should be checking against SUCCESS. More granular failures
358 // may be added in future.
359 int callbackResultCode =
360 packageTrackerStatus ? Callback.SUCCESS : Callback.ERROR_UNKNOWN_FAILURE;
Neil Fullerd857f672017-07-20 11:00:35 +0100361 EventLogTags.writeTimezoneUninstallComplete(
Neil Fuller8e27c922017-09-14 09:34:56 +0100362 toStringOrNull(mCheckToken), callbackResultCode);
363 sendFinishedStatus(mCallback, callbackResultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000364 } catch (Exception e) {
Neil Fullerd857f672017-07-20 11:00:35 +0100365 EventLogTags.writeTimezoneUninstallComplete(
366 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000367 Slog.w(TAG, "Failed to uninstall distro.", e);
368 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
369 } finally {
370 // Notify the package tracker that the operation is now complete.
Neil Fuller8e27c922017-09-14 09:34:56 +0100371 mPackageTracker.recordCheckResult(mCheckToken, packageTrackerStatus);
Neil Fuller68f66662017-03-16 18:32:21 +0000372
373 mOperationInProgress.set(false);
374 }
375 }
376 }
377
378 private void sendFinishedStatus(ICallback callback, int resultCode) {
379 try {
380 callback.onFinished(resultCode);
381 } catch (RemoteException e) {
382 Slog.e(TAG, "Unable to notify observer of result", e);
383 }
384 }
385
386 @Override
387 public void requestNothing(byte[] checkTokenBytes, boolean success) {
388 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
389 CheckToken checkToken = null;
390 if (checkTokenBytes != null) {
391 checkToken = createCheckTokenOrThrow(checkTokenBytes);
392 }
Neil Fullerd857f672017-07-20 11:00:35 +0100393 EventLogTags.writeTimezoneRequestNothing(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000394 mPackageTracker.recordCheckResult(checkToken, success);
Neil Fullerd857f672017-07-20 11:00:35 +0100395 EventLogTags.writeTimezoneNothingComplete(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000396 }
397
Neil Fuller87b11282017-06-23 16:43:45 +0100398 @Override
399 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
400 if (!mPermissionHelper.checkDumpPermission(TAG, pw)) {
401 return;
402 }
403
404 RulesState rulesState = getRulesStateInternal();
405 if (args != null && args.length == 2) {
406 // Formatting options used for automated tests. The format is less free-form than
407 // the -format options, which are intended to be easier to parse.
408 if ("-format_state".equals(args[0]) && args[1] != null) {
409 for (char c : args[1].toCharArray()) {
410 switch (c) {
Neil Fullera47c3632017-07-26 13:56:25 +0100411 case 'p': {
412 // Report operation in progress
413 String value = "Unknown";
414 if (rulesState != null) {
415 value = Boolean.toString(rulesState.isOperationInProgress());
Neil Fuller87b11282017-06-23 16:43:45 +0100416 }
Neil Fullera47c3632017-07-26 13:56:25 +0100417 pw.println("Operation in progress: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100418 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100419 }
420 case 's': {
421 // Report system image rules version
422 String value = "Unknown";
423 if (rulesState != null) {
424 value = rulesState.getSystemRulesVersion();
425 }
426 pw.println("System rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100427 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100428 }
429 case 'c': {
430 // Report current installation state
431 String value = "Unknown";
432 if (rulesState != null) {
433 value = distroStatusToString(rulesState.getDistroStatus());
434 }
435 pw.println("Current install state: " + value);
436 break;
437 }
438 case 'i': {
439 // Report currently installed version
440 String value = "Unknown";
441 if (rulesState != null) {
442 DistroRulesVersion installedRulesVersion =
443 rulesState.getInstalledDistroRulesVersion();
444 if (installedRulesVersion == null) {
445 value = "<None>";
446 } else {
447 value = installedRulesVersion.toDumpString();
448 }
449 }
450 pw.println("Installed rules version: " + value);
451 break;
452 }
453 case 'o': {
454 // Report staged operation type
455 String value = "Unknown";
456 if (rulesState != null) {
457 int stagedOperationType = rulesState.getStagedOperationType();
458 value = stagedOperationToString(stagedOperationType);
459 }
460 pw.println("Staged operation: " + value);
461 break;
462 }
463 case 't': {
Neil Fuller87b11282017-06-23 16:43:45 +0100464 // Report staged version (i.e. the one that will be installed next boot
465 // if the staged operation is an install).
Neil Fullera47c3632017-07-26 13:56:25 +0100466 String value = "Unknown";
467 if (rulesState != null) {
468 DistroRulesVersion stagedDistroRulesVersion =
469 rulesState.getStagedDistroRulesVersion();
470 if (stagedDistroRulesVersion == null) {
471 value = "<None>";
472 } else {
473 value = stagedDistroRulesVersion.toDumpString();
474 }
Neil Fuller87b11282017-06-23 16:43:45 +0100475 }
Neil Fullera47c3632017-07-26 13:56:25 +0100476 pw.println("Staged rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100477 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100478 }
479 case 'a': {
Neil Fuller87b11282017-06-23 16:43:45 +0100480 // Report the active rules version (i.e. the rules in use by the current
481 // process).
482 pw.println("Active rules version (ICU, libcore): "
483 + ICU.getTZDataVersion() + ","
484 + ZoneInfoDB.getInstance().getVersion());
485 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100486 }
487 default: {
Neil Fuller87b11282017-06-23 16:43:45 +0100488 pw.println("Unknown option: " + c);
Neil Fullera47c3632017-07-26 13:56:25 +0100489 }
Neil Fuller87b11282017-06-23 16:43:45 +0100490 }
491 }
492 return;
493 }
494 }
495
496 pw.println("RulesManagerService state: " + toString());
497 pw.println("Active rules version (ICU, libcore): " + ICU.getTZDataVersion() + ","
498 + ZoneInfoDB.getInstance().getVersion());
Neil Fullerd857f672017-07-20 11:00:35 +0100499 pw.println("Distro state: " + rulesState.toString());
Neil Fuller87b11282017-06-23 16:43:45 +0100500 mPackageTracker.dump(pw);
501 }
502
Neil Fullercd1a1092017-09-13 21:59:59 +0100503 /**
504 * Called when the device is considered idle.
505 */
506 void notifyIdle() {
507 // No package has changed: we are just triggering because the device is idle and there
508 // *might* be work to do.
509 final boolean packageChanged = false;
510 mPackageTracker.triggerUpdateIfNeeded(packageChanged);
511 }
512
Neil Fuller87b11282017-06-23 16:43:45 +0100513 @Override
514 public String toString() {
515 return "RulesManagerService{" +
516 "mOperationInProgress=" + mOperationInProgress +
517 '}';
518 }
519
Neil Fuller68f66662017-03-16 18:32:21 +0000520 private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) {
521 CheckToken checkToken;
522 try {
523 checkToken = CheckToken.fromByteArray(checkTokenBytes);
524 } catch (IOException e) {
525 throw new IllegalArgumentException("Unable to read token bytes "
526 + Arrays.toString(checkTokenBytes), e);
527 }
528 return checkToken;
529 }
Neil Fuller87b11282017-06-23 16:43:45 +0100530
531 private static String distroStatusToString(int distroStatus) {
532 switch(distroStatus) {
533 case DISTRO_STATUS_NONE:
534 return "None";
535 case DISTRO_STATUS_INSTALLED:
536 return "Installed";
537 case DISTRO_STATUS_UNKNOWN:
538 default:
539 return "Unknown";
540 }
541 }
542
543 private static String stagedOperationToString(int stagedOperationType) {
544 switch(stagedOperationType) {
545 case STAGED_OPERATION_NONE:
546 return "None";
547 case STAGED_OPERATION_UNINSTALL:
548 return "Uninstall";
549 case STAGED_OPERATION_INSTALL:
550 return "Install";
551 case STAGED_OPERATION_UNKNOWN:
552 default:
553 return "Unknown";
554 }
555 }
Neil Fullerd857f672017-07-20 11:00:35 +0100556
557 private static String toStringOrNull(Object obj) {
558 return obj == null ? null : obj.toString();
559 }
Neil Fuller68f66662017-03-16 18:32:21 +0000560}