blob: da0a794c74f376c3d2c2a84789519819b6dd5102 [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 Fuller0ac8df02018-11-16 16:19:26 +000050import libcore.timezone.TzDataSetVersion;
Neil Fuller164394d2018-11-16 21:14:13 +000051import libcore.timezone.TimeZoneFinder;
Neil Fuller87b11282017-06-23 16:43:45 +010052import libcore.util.ZoneInfoDB;
53
54import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
55import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
56import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN;
57import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL;
58import static android.app.timezone.RulesState.STAGED_OPERATION_NONE;
59import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL;
60import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +000061
Neil Fuller68f66662017-03-16 18:32:21 +000062public final class RulesManagerService extends IRulesManager.Stub {
63
64 private static final String TAG = "timezone.RulesManagerService";
65
66 /** The distro format supported by this device. */
67 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
68 static final DistroFormatVersion DISTRO_FORMAT_VERSION_SUPPORTED =
69 new DistroFormatVersion(
Neil Fuller0ac8df02018-11-16 16:19:26 +000070 TzDataSetVersion.currentFormatMajorVersion(),
71 TzDataSetVersion.currentFormatMinorVersion());
Neil Fuller68f66662017-03-16 18:32:21 +000072
73 public static class Lifecycle extends SystemService {
Neil Fuller68f66662017-03-16 18:32:21 +000074 public Lifecycle(Context context) {
75 super(context);
76 }
77
78 @Override
79 public void onStart() {
Neil Fullercd1a1092017-09-13 21:59:59 +010080 RulesManagerService service = RulesManagerService.create(getContext());
81 service.start();
Neil Fuller68f66662017-03-16 18:32:21 +000082
Neil Fullercd1a1092017-09-13 21:59:59 +010083 // Publish the binder service so it can be accessed from other (appropriately
84 // permissioned) processes.
85 publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, service);
86
87 // Publish the service instance locally so we can use it directly from within the system
88 // server from TimeZoneUpdateIdler.
89 publishLocalService(RulesManagerService.class, service);
Neil Fuller68f66662017-03-16 18:32:21 +000090 }
91 }
92
93 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
94 static final String REQUIRED_UPDATER_PERMISSION =
95 android.Manifest.permission.UPDATE_TIME_ZONE_RULES;
Neil Fullerb214bc42017-12-18 16:57:22 +000096 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
97 static final String REQUIRED_QUERY_PERMISSION =
98 android.Manifest.permission.QUERY_TIME_ZONE_RULES;
Neil Fuller68f66662017-03-16 18:32:21 +000099 private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata");
100 private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
101
102 private final AtomicBoolean mOperationInProgress = new AtomicBoolean(false);
103 private final PermissionHelper mPermissionHelper;
104 private final PackageTracker mPackageTracker;
105 private final Executor mExecutor;
Neil Fullerb1442272017-12-18 15:59:50 +0000106 private final RulesManagerIntentHelper mIntentHelper;
Neil Fuller68f66662017-03-16 18:32:21 +0000107 private final TimeZoneDistroInstaller mInstaller;
Neil Fuller68f66662017-03-16 18:32:21 +0000108
109 private static RulesManagerService create(Context context) {
110 RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context);
111 return new RulesManagerService(
112 helper /* permissionHelper */,
113 helper /* executor */,
Neil Fullerb1442272017-12-18 15:59:50 +0000114 helper /* intentHelper */,
Neil Fuller68f66662017-03-16 18:32:21 +0000115 PackageTracker.create(context),
116 new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR));
117 }
118
119 // A constructor that can be used by tests to supply mocked / faked dependencies.
Neil Fullerb1442272017-12-18 15:59:50 +0000120 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
121 RulesManagerService(PermissionHelper permissionHelper, Executor executor,
122 RulesManagerIntentHelper intentHelper, PackageTracker packageTracker,
Neil Fuller68f66662017-03-16 18:32:21 +0000123 TimeZoneDistroInstaller timeZoneDistroInstaller) {
124 mPermissionHelper = permissionHelper;
125 mExecutor = executor;
Neil Fullerb1442272017-12-18 15:59:50 +0000126 mIntentHelper = intentHelper;
Neil Fuller68f66662017-03-16 18:32:21 +0000127 mPackageTracker = packageTracker;
128 mInstaller = timeZoneDistroInstaller;
129 }
130
131 public void start() {
Neil Fuller35822592017-12-11 14:39:03 +0000132 // Return value deliberately ignored: no action required on failure to start.
Neil Fuller68f66662017-03-16 18:32:21 +0000133 mPackageTracker.start();
134 }
135
136 @Override // Binder call
137 public RulesState getRulesState() {
Neil Fullerb214bc42017-12-18 16:57:22 +0000138 mPermissionHelper.enforceCallerHasPermission(REQUIRED_QUERY_PERMISSION);
Neil Fuller68f66662017-03-16 18:32:21 +0000139
Neil Fuller87b11282017-06-23 16:43:45 +0100140 return getRulesStateInternal();
141 }
142
143 /** Like {@link #getRulesState()} without the permission check. */
144 private RulesState getRulesStateInternal() {
Neil Fuller68f66662017-03-16 18:32:21 +0000145 synchronized(this) {
146 String systemRulesVersion;
147 try {
148 systemRulesVersion = mInstaller.getSystemRulesVersion();
149 } catch (IOException e) {
150 Slog.w(TAG, "Failed to read system rules", e);
151 return null;
152 }
153
Neil Fullerad3e1332018-01-22 12:17:14 +0000154 // Determine the installed distro state. This should be possible regardless of whether
155 // there's an operation in progress.
156 DistroVersion installedDistroVersion;
157 int distroStatus = DISTRO_STATUS_UNKNOWN;
158 DistroRulesVersion installedDistroRulesVersion = null;
159 try {
160 installedDistroVersion = mInstaller.getInstalledDistroVersion();
161 if (installedDistroVersion == null) {
162 distroStatus = DISTRO_STATUS_NONE;
163 installedDistroRulesVersion = null;
164 } else {
165 distroStatus = DISTRO_STATUS_INSTALLED;
166 installedDistroRulesVersion = new DistroRulesVersion(
167 installedDistroVersion.rulesVersion,
168 installedDistroVersion.revision);
169 }
170 } catch (DistroException | IOException e) {
171 Slog.w(TAG, "Failed to read installed distro.", e);
172 }
173
Neil Fuller68f66662017-03-16 18:32:21 +0000174 boolean operationInProgress = this.mOperationInProgress.get();
175
176 // Determine the staged operation status, if possible.
177 DistroRulesVersion stagedDistroRulesVersion = null;
Neil Fuller87b11282017-06-23 16:43:45 +0100178 int stagedOperationStatus = STAGED_OPERATION_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +0000179 if (!operationInProgress) {
180 StagedDistroOperation stagedDistroOperation;
181 try {
182 stagedDistroOperation = mInstaller.getStagedDistroOperation();
183 if (stagedDistroOperation == null) {
Neil Fuller87b11282017-06-23 16:43:45 +0100184 stagedOperationStatus = STAGED_OPERATION_NONE;
Neil Fuller68f66662017-03-16 18:32:21 +0000185 } else if (stagedDistroOperation.isUninstall) {
Neil Fuller87b11282017-06-23 16:43:45 +0100186 stagedOperationStatus = STAGED_OPERATION_UNINSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000187 } else {
188 // Must be an install.
Neil Fuller87b11282017-06-23 16:43:45 +0100189 stagedOperationStatus = STAGED_OPERATION_INSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000190 DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion;
191 stagedDistroRulesVersion = new DistroRulesVersion(
192 stagedDistroVersion.rulesVersion,
193 stagedDistroVersion.revision);
194 }
195 } catch (DistroException | IOException e) {
196 Slog.w(TAG, "Failed to read staged distro.", e);
197 }
198 }
Neil Fuller68f66662017-03-16 18:32:21 +0000199 return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
200 operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
201 distroStatus, installedDistroRulesVersion);
202 }
203 }
204
205 @Override
Neil Fuller54525bf2017-06-22 14:10:29 +0100206 public int requestInstall(ParcelFileDescriptor distroParcelFileDescriptor,
207 byte[] checkTokenBytes, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000208
Neil Fuller54525bf2017-06-22 14:10:29 +0100209 boolean closeParcelFileDescriptorOnExit = true;
210 try {
211 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
Neil Fuller68f66662017-03-16 18:32:21 +0000212
Neil Fuller54525bf2017-06-22 14:10:29 +0100213 CheckToken checkToken = null;
214 if (checkTokenBytes != null) {
215 checkToken = createCheckTokenOrThrow(checkTokenBytes);
216 }
Neil Fullerd857f672017-07-20 11:00:35 +0100217 EventLogTags.writeTimezoneRequestInstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000218
Neil Fuller54525bf2017-06-22 14:10:29 +0100219 synchronized (this) {
220 if (distroParcelFileDescriptor == null) {
221 throw new NullPointerException("distroParcelFileDescriptor == null");
222 }
223 if (callback == null) {
224 throw new NullPointerException("observer == null");
225 }
226 if (mOperationInProgress.get()) {
227 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
228 }
229 mOperationInProgress.set(true);
230
231 // Execute the install asynchronously.
232 mExecutor.execute(
233 new InstallRunnable(distroParcelFileDescriptor, checkToken, callback));
234
235 // The InstallRunnable now owns the ParcelFileDescriptor, so it will close it after
236 // it executes (and we do not have to).
237 closeParcelFileDescriptorOnExit = false;
238
239 return RulesManager.SUCCESS;
240 }
241 } finally {
242 // We should close() the local ParcelFileDescriptor we were passed if it hasn't been
243 // passed to another thread to handle.
244 if (distroParcelFileDescriptor != null && closeParcelFileDescriptorOnExit) {
245 try {
246 distroParcelFileDescriptor.close();
247 } catch (IOException e) {
248 Slog.w(TAG, "Failed to close distroParcelFileDescriptor", e);
249 }
250 }
Neil Fuller68f66662017-03-16 18:32:21 +0000251 }
252 }
253
254 private class InstallRunnable implements Runnable {
255
Neil Fuller54525bf2017-06-22 14:10:29 +0100256 private final ParcelFileDescriptor mDistroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000257 private final CheckToken mCheckToken;
258 private final ICallback mCallback;
259
Neil Fuller54525bf2017-06-22 14:10:29 +0100260 InstallRunnable(ParcelFileDescriptor distroParcelFileDescriptor, CheckToken checkToken,
261 ICallback callback) {
262 mDistroParcelFileDescriptor = distroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000263 mCheckToken = checkToken;
264 mCallback = callback;
265 }
266
267 @Override
268 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100269 EventLogTags.writeTimezoneInstallStarted(toStringOrNull(mCheckToken));
270
Neil Fuller54525bf2017-06-22 14:10:29 +0100271 boolean success = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000272 // Adopt the ParcelFileDescriptor into this try-with-resources so it is closed
273 // when we are done.
Neil Fuller54525bf2017-06-22 14:10:29 +0100274 try (ParcelFileDescriptor pfd = mDistroParcelFileDescriptor) {
275 // The ParcelFileDescriptor owns the underlying FileDescriptor and we'll close
276 // it at the end of the try-with-resources.
277 final boolean isFdOwner = false;
278 InputStream is = new FileInputStream(pfd.getFileDescriptor(), isFdOwner);
279
280 TimeZoneDistro distro = new TimeZoneDistro(is);
Neil Fullerfe3b1182017-06-19 12:56:08 +0100281 int installerResult = mInstaller.stageInstallWithErrorCode(distro);
Neil Fullerb1442272017-12-18 15:59:50 +0000282
283 // Notify interested parties that something is staged.
284 sendInstallNotificationIntentIfRequired(installerResult);
285
Neil Fuller68f66662017-03-16 18:32:21 +0000286 int resultCode = mapInstallerResultToApiCode(installerResult);
Neil Fullerd857f672017-07-20 11:00:35 +0100287 EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000288 sendFinishedStatus(mCallback, resultCode);
289
290 // All the installer failure modes are currently non-recoverable and won't be
291 // improved by trying again. Therefore success = true.
292 success = true;
293 } catch (Exception e) {
294 Slog.w(TAG, "Failed to install distro.", e);
Neil Fullerd857f672017-07-20 11:00:35 +0100295 EventLogTags.writeTimezoneInstallComplete(
296 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000297 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
298 } finally {
299 // Notify the package tracker that the operation is now complete.
300 mPackageTracker.recordCheckResult(mCheckToken, success);
301
302 mOperationInProgress.set(false);
303 }
304 }
305
Neil Fullerb1442272017-12-18 15:59:50 +0000306 private void sendInstallNotificationIntentIfRequired(int installerResult) {
307 if (installerResult == TimeZoneDistroInstaller.INSTALL_SUCCESS) {
308 mIntentHelper.sendTimeZoneOperationStaged();
309 }
310 }
311
Neil Fuller68f66662017-03-16 18:32:21 +0000312 private int mapInstallerResultToApiCode(int installerResult) {
313 switch (installerResult) {
314 case TimeZoneDistroInstaller.INSTALL_SUCCESS:
315 return Callback.SUCCESS;
316 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE:
317 return Callback.ERROR_INSTALL_BAD_DISTRO_STRUCTURE;
318 case TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD:
319 return Callback.ERROR_INSTALL_RULES_TOO_OLD;
320 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION:
321 return Callback.ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION;
322 case TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR:
323 return Callback.ERROR_INSTALL_VALIDATION_ERROR;
324 default:
325 return Callback.ERROR_UNKNOWN_FAILURE;
326 }
327 }
328 }
329
330 @Override
331 public int requestUninstall(byte[] checkTokenBytes, ICallback callback) {
332 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
333
334 CheckToken checkToken = null;
335 if (checkTokenBytes != null) {
336 checkToken = createCheckTokenOrThrow(checkTokenBytes);
337 }
Neil Fullerd857f672017-07-20 11:00:35 +0100338 EventLogTags.writeTimezoneRequestUninstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000339 synchronized(this) {
340 if (callback == null) {
341 throw new NullPointerException("callback == null");
342 }
343
344 if (mOperationInProgress.get()) {
345 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
346 }
347 mOperationInProgress.set(true);
348
349 // Execute the uninstall asynchronously.
350 mExecutor.execute(new UninstallRunnable(checkToken, callback));
351
352 return RulesManager.SUCCESS;
353 }
354 }
355
356 private class UninstallRunnable implements Runnable {
357
358 private final CheckToken mCheckToken;
359 private final ICallback mCallback;
360
Neil Fullera47c3632017-07-26 13:56:25 +0100361 UninstallRunnable(CheckToken checkToken, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000362 mCheckToken = checkToken;
363 mCallback = callback;
364 }
365
366 @Override
367 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100368 EventLogTags.writeTimezoneUninstallStarted(toStringOrNull(mCheckToken));
Neil Fuller8e27c922017-09-14 09:34:56 +0100369 boolean packageTrackerStatus = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000370 try {
Neil Fuller8e27c922017-09-14 09:34:56 +0100371 int uninstallResult = mInstaller.stageUninstall();
Neil Fullerb1442272017-12-18 15:59:50 +0000372
373 // Notify interested parties that something is staged.
374 sendUninstallNotificationIntentIfRequired(uninstallResult);
375
Neil Fuller8e27c922017-09-14 09:34:56 +0100376 packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS
377 || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
378
379 // Right now we just have Callback.SUCCESS / Callback.ERROR_UNKNOWN_FAILURE for
380 // uninstall. All clients should be checking against SUCCESS. More granular failures
381 // may be added in future.
382 int callbackResultCode =
383 packageTrackerStatus ? Callback.SUCCESS : Callback.ERROR_UNKNOWN_FAILURE;
Neil Fullerd857f672017-07-20 11:00:35 +0100384 EventLogTags.writeTimezoneUninstallComplete(
Neil Fuller8e27c922017-09-14 09:34:56 +0100385 toStringOrNull(mCheckToken), callbackResultCode);
386 sendFinishedStatus(mCallback, callbackResultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000387 } catch (Exception e) {
Neil Fullerd857f672017-07-20 11:00:35 +0100388 EventLogTags.writeTimezoneUninstallComplete(
389 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000390 Slog.w(TAG, "Failed to uninstall distro.", e);
391 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
392 } finally {
393 // Notify the package tracker that the operation is now complete.
Neil Fuller8e27c922017-09-14 09:34:56 +0100394 mPackageTracker.recordCheckResult(mCheckToken, packageTrackerStatus);
Neil Fuller68f66662017-03-16 18:32:21 +0000395
396 mOperationInProgress.set(false);
397 }
398 }
Neil Fullerb1442272017-12-18 15:59:50 +0000399
400 private void sendUninstallNotificationIntentIfRequired(int uninstallResult) {
401 switch (uninstallResult) {
402 case TimeZoneDistroInstaller.UNINSTALL_SUCCESS:
403 mIntentHelper.sendTimeZoneOperationStaged();
404 break;
405 case TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED:
406 mIntentHelper.sendTimeZoneOperationUnstaged();
407 break;
408 case TimeZoneDistroInstaller.UNINSTALL_FAIL:
409 default:
410 // No-op - unknown or nothing to notify about.
411 }
412 }
Neil Fuller68f66662017-03-16 18:32:21 +0000413 }
414
415 private void sendFinishedStatus(ICallback callback, int resultCode) {
416 try {
417 callback.onFinished(resultCode);
418 } catch (RemoteException e) {
419 Slog.e(TAG, "Unable to notify observer of result", e);
420 }
421 }
422
423 @Override
424 public void requestNothing(byte[] checkTokenBytes, boolean success) {
425 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
426 CheckToken checkToken = null;
427 if (checkTokenBytes != null) {
428 checkToken = createCheckTokenOrThrow(checkTokenBytes);
429 }
Neil Fullerd857f672017-07-20 11:00:35 +0100430 EventLogTags.writeTimezoneRequestNothing(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000431 mPackageTracker.recordCheckResult(checkToken, success);
Neil Fullerd857f672017-07-20 11:00:35 +0100432 EventLogTags.writeTimezoneNothingComplete(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000433 }
434
Neil Fuller87b11282017-06-23 16:43:45 +0100435 @Override
436 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
437 if (!mPermissionHelper.checkDumpPermission(TAG, pw)) {
438 return;
439 }
440
441 RulesState rulesState = getRulesStateInternal();
442 if (args != null && args.length == 2) {
443 // Formatting options used for automated tests. The format is less free-form than
444 // the -format options, which are intended to be easier to parse.
445 if ("-format_state".equals(args[0]) && args[1] != null) {
446 for (char c : args[1].toCharArray()) {
447 switch (c) {
Neil Fullera47c3632017-07-26 13:56:25 +0100448 case 'p': {
449 // Report operation in progress
450 String value = "Unknown";
451 if (rulesState != null) {
452 value = Boolean.toString(rulesState.isOperationInProgress());
Neil Fuller87b11282017-06-23 16:43:45 +0100453 }
Neil Fullera47c3632017-07-26 13:56:25 +0100454 pw.println("Operation in progress: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100455 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100456 }
457 case 's': {
458 // Report system image rules version
459 String value = "Unknown";
460 if (rulesState != null) {
461 value = rulesState.getSystemRulesVersion();
462 }
463 pw.println("System rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100464 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100465 }
466 case 'c': {
467 // Report current installation state
468 String value = "Unknown";
469 if (rulesState != null) {
470 value = distroStatusToString(rulesState.getDistroStatus());
471 }
472 pw.println("Current install state: " + value);
473 break;
474 }
475 case 'i': {
476 // Report currently installed version
477 String value = "Unknown";
478 if (rulesState != null) {
479 DistroRulesVersion installedRulesVersion =
480 rulesState.getInstalledDistroRulesVersion();
481 if (installedRulesVersion == null) {
482 value = "<None>";
483 } else {
484 value = installedRulesVersion.toDumpString();
485 }
486 }
487 pw.println("Installed rules version: " + value);
488 break;
489 }
490 case 'o': {
491 // Report staged operation type
492 String value = "Unknown";
493 if (rulesState != null) {
494 int stagedOperationType = rulesState.getStagedOperationType();
495 value = stagedOperationToString(stagedOperationType);
496 }
497 pw.println("Staged operation: " + value);
498 break;
499 }
500 case 't': {
Neil Fuller87b11282017-06-23 16:43:45 +0100501 // Report staged version (i.e. the one that will be installed next boot
502 // if the staged operation is an install).
Neil Fullera47c3632017-07-26 13:56:25 +0100503 String value = "Unknown";
504 if (rulesState != null) {
505 DistroRulesVersion stagedDistroRulesVersion =
506 rulesState.getStagedDistroRulesVersion();
507 if (stagedDistroRulesVersion == null) {
508 value = "<None>";
509 } else {
510 value = stagedDistroRulesVersion.toDumpString();
511 }
Neil Fuller87b11282017-06-23 16:43:45 +0100512 }
Neil Fullera47c3632017-07-26 13:56:25 +0100513 pw.println("Staged rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100514 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100515 }
516 case 'a': {
Neil Fuller87b11282017-06-23 16:43:45 +0100517 // Report the active rules version (i.e. the rules in use by the current
518 // process).
Neil Fullerc9036142017-10-04 17:04:38 +0100519 pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
Neil Fuller87b11282017-06-23 16:43:45 +0100520 + ICU.getTZDataVersion() + ","
Neil Fullerc9036142017-10-04 17:04:38 +0100521 + ZoneInfoDB.getInstance().getVersion() + ","
522 + TimeZoneFinder.getInstance().getIanaVersion());
Neil Fuller87b11282017-06-23 16:43:45 +0100523 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100524 }
525 default: {
Neil Fuller87b11282017-06-23 16:43:45 +0100526 pw.println("Unknown option: " + c);
Neil Fullera47c3632017-07-26 13:56:25 +0100527 }
Neil Fuller87b11282017-06-23 16:43:45 +0100528 }
529 }
530 return;
531 }
532 }
533
534 pw.println("RulesManagerService state: " + toString());
Neil Fullerc9036142017-10-04 17:04:38 +0100535 pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
536 + ICU.getTZDataVersion() + ","
537 + ZoneInfoDB.getInstance().getVersion() + ","
538 + TimeZoneFinder.getInstance().getIanaVersion());
Neil Fullerd857f672017-07-20 11:00:35 +0100539 pw.println("Distro state: " + rulesState.toString());
Neil Fuller87b11282017-06-23 16:43:45 +0100540 mPackageTracker.dump(pw);
541 }
542
Neil Fullercd1a1092017-09-13 21:59:59 +0100543 /**
544 * Called when the device is considered idle.
545 */
546 void notifyIdle() {
547 // No package has changed: we are just triggering because the device is idle and there
548 // *might* be work to do.
549 final boolean packageChanged = false;
550 mPackageTracker.triggerUpdateIfNeeded(packageChanged);
551 }
552
Neil Fuller87b11282017-06-23 16:43:45 +0100553 @Override
554 public String toString() {
555 return "RulesManagerService{" +
556 "mOperationInProgress=" + mOperationInProgress +
557 '}';
558 }
559
Neil Fuller68f66662017-03-16 18:32:21 +0000560 private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) {
561 CheckToken checkToken;
562 try {
563 checkToken = CheckToken.fromByteArray(checkTokenBytes);
564 } catch (IOException e) {
565 throw new IllegalArgumentException("Unable to read token bytes "
566 + Arrays.toString(checkTokenBytes), e);
567 }
568 return checkToken;
569 }
Neil Fuller87b11282017-06-23 16:43:45 +0100570
571 private static String distroStatusToString(int distroStatus) {
572 switch(distroStatus) {
573 case DISTRO_STATUS_NONE:
574 return "None";
575 case DISTRO_STATUS_INSTALLED:
576 return "Installed";
577 case DISTRO_STATUS_UNKNOWN:
578 default:
579 return "Unknown";
580 }
581 }
582
583 private static String stagedOperationToString(int stagedOperationType) {
584 switch(stagedOperationType) {
585 case STAGED_OPERATION_NONE:
586 return "None";
587 case STAGED_OPERATION_UNINSTALL:
588 return "Uninstall";
589 case STAGED_OPERATION_INSTALL:
590 return "Install";
591 case STAGED_OPERATION_UNKNOWN:
592 default:
593 return "Unknown";
594 }
595 }
Neil Fullerd857f672017-07-20 11:00:35 +0100596
597 private static String toStringOrNull(Object obj) {
598 return obj == null ? null : obj.toString();
599 }
Neil Fuller68f66662017-03-16 18:32:21 +0000600}