blob: 296a6526f5a652aa5389e8be86b8265198bfc4ca [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
Neil Fullera1ae0252019-02-18 10:49:15 +000019import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
20import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
21import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN;
22import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL;
23import static android.app.timezone.RulesState.STAGED_OPERATION_NONE;
24import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL;
25import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +000026
27import android.app.timezone.Callback;
28import android.app.timezone.DistroFormatVersion;
29import android.app.timezone.DistroRulesVersion;
30import android.app.timezone.ICallback;
31import android.app.timezone.IRulesManager;
32import android.app.timezone.RulesManager;
33import android.app.timezone.RulesState;
34import android.content.Context;
35import android.os.ParcelFileDescriptor;
36import android.os.RemoteException;
37import android.util.Slog;
38
Neil Fullera1ae0252019-02-18 10:49:15 +000039import com.android.internal.annotations.VisibleForTesting;
40import com.android.server.EventLogTags;
41import com.android.server.SystemService;
42import com.android.timezone.distro.DistroException;
43import com.android.timezone.distro.DistroVersion;
44import com.android.timezone.distro.StagedDistroOperation;
45import com.android.timezone.distro.TimeZoneDistro;
46import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
47
48import libcore.icu.ICU;
49import libcore.timezone.TimeZoneDataFiles;
50import libcore.timezone.TimeZoneFinder;
51import libcore.timezone.TzDataSetVersion;
52import libcore.timezone.ZoneInfoDB;
53
Neil Fuller68f66662017-03-16 18:32:21 +000054import java.io.File;
Neil Fuller87b11282017-06-23 16:43:45 +010055import java.io.FileDescriptor;
Neil Fuller54525bf2017-06-22 14:10:29 +010056import java.io.FileInputStream;
Neil Fuller68f66662017-03-16 18:32:21 +000057import java.io.IOException;
Neil Fuller54525bf2017-06-22 14:10:29 +010058import java.io.InputStream;
Neil Fuller87b11282017-06-23 16:43:45 +010059import java.io.PrintWriter;
Neil Fuller68f66662017-03-16 18:32:21 +000060import java.util.Arrays;
61import java.util.concurrent.Executor;
62import java.util.concurrent.atomic.AtomicBoolean;
Neil Fuller68f66662017-03-16 18:32:21 +000063
Neil Fuller68f66662017-03-16 18:32:21 +000064public final class RulesManagerService extends IRulesManager.Stub {
65
66 private static final String TAG = "timezone.RulesManagerService";
67
68 /** The distro format supported by this device. */
69 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
70 static final DistroFormatVersion DISTRO_FORMAT_VERSION_SUPPORTED =
71 new DistroFormatVersion(
Neil Fuller0ac8df02018-11-16 16:19:26 +000072 TzDataSetVersion.currentFormatMajorVersion(),
73 TzDataSetVersion.currentFormatMinorVersion());
Neil Fuller68f66662017-03-16 18:32:21 +000074
75 public static class Lifecycle extends SystemService {
Neil Fuller68f66662017-03-16 18:32:21 +000076 public Lifecycle(Context context) {
77 super(context);
78 }
79
80 @Override
81 public void onStart() {
Neil Fullercd1a1092017-09-13 21:59:59 +010082 RulesManagerService service = RulesManagerService.create(getContext());
83 service.start();
Neil Fuller68f66662017-03-16 18:32:21 +000084
Neil Fullercd1a1092017-09-13 21:59:59 +010085 // Publish the binder service so it can be accessed from other (appropriately
86 // permissioned) processes.
87 publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, service);
88
89 // Publish the service instance locally so we can use it directly from within the system
90 // server from TimeZoneUpdateIdler.
91 publishLocalService(RulesManagerService.class, service);
Neil Fuller68f66662017-03-16 18:32:21 +000092 }
93 }
94
95 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
96 static final String REQUIRED_UPDATER_PERMISSION =
97 android.Manifest.permission.UPDATE_TIME_ZONE_RULES;
Neil Fullerb214bc42017-12-18 16:57:22 +000098 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
99 static final String REQUIRED_QUERY_PERMISSION =
100 android.Manifest.permission.QUERY_TIME_ZONE_RULES;
Neil Fuller68f66662017-03-16 18:32:21 +0000101
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);
Neil Fullera1ae0252019-02-18 10:49:15 +0000111 File baseVersionFile = new File(TimeZoneDataFiles.getRuntimeModuleTzVersionFile());
112 File tzDataDir = new File(TimeZoneDataFiles.getDataTimeZoneRootDir());
Neil Fuller68f66662017-03-16 18:32:21 +0000113 return new RulesManagerService(
114 helper /* permissionHelper */,
115 helper /* executor */,
Neil Fullerb1442272017-12-18 15:59:50 +0000116 helper /* intentHelper */,
Neil Fuller68f66662017-03-16 18:32:21 +0000117 PackageTracker.create(context),
Neil Fullera1ae0252019-02-18 10:49:15 +0000118 new TimeZoneDistroInstaller(TAG, baseVersionFile, tzDataDir));
Neil Fuller68f66662017-03-16 18:32:21 +0000119 }
120
121 // A constructor that can be used by tests to supply mocked / faked dependencies.
Neil Fullerb1442272017-12-18 15:59:50 +0000122 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
123 RulesManagerService(PermissionHelper permissionHelper, Executor executor,
124 RulesManagerIntentHelper intentHelper, PackageTracker packageTracker,
Neil Fuller68f66662017-03-16 18:32:21 +0000125 TimeZoneDistroInstaller timeZoneDistroInstaller) {
126 mPermissionHelper = permissionHelper;
127 mExecutor = executor;
Neil Fullerb1442272017-12-18 15:59:50 +0000128 mIntentHelper = intentHelper;
Neil Fuller68f66662017-03-16 18:32:21 +0000129 mPackageTracker = packageTracker;
130 mInstaller = timeZoneDistroInstaller;
131 }
132
133 public void start() {
Neil Fuller35822592017-12-11 14:39:03 +0000134 // Return value deliberately ignored: no action required on failure to start.
Neil Fuller68f66662017-03-16 18:32:21 +0000135 mPackageTracker.start();
136 }
137
138 @Override // Binder call
139 public RulesState getRulesState() {
Neil Fullerb214bc42017-12-18 16:57:22 +0000140 mPermissionHelper.enforceCallerHasPermission(REQUIRED_QUERY_PERMISSION);
Neil Fuller68f66662017-03-16 18:32:21 +0000141
Neil Fuller87b11282017-06-23 16:43:45 +0100142 return getRulesStateInternal();
143 }
144
145 /** Like {@link #getRulesState()} without the permission check. */
146 private RulesState getRulesStateInternal() {
Neil Fuller68f66662017-03-16 18:32:21 +0000147 synchronized(this) {
Neil Fullera1ae0252019-02-18 10:49:15 +0000148 TzDataSetVersion baseVersion;
Neil Fuller68f66662017-03-16 18:32:21 +0000149 try {
Neil Fullera1ae0252019-02-18 10:49:15 +0000150 baseVersion = mInstaller.readBaseVersion();
Neil Fuller68f66662017-03-16 18:32:21 +0000151 } catch (IOException e) {
Neil Fullera1ae0252019-02-18 10:49:15 +0000152 Slog.w(TAG, "Failed to read base rules version", e);
Neil Fuller68f66662017-03-16 18:32:21 +0000153 return null;
154 }
155
Neil Fullerad3e1332018-01-22 12:17:14 +0000156 // Determine the installed distro state. This should be possible regardless of whether
157 // there's an operation in progress.
158 DistroVersion installedDistroVersion;
159 int distroStatus = DISTRO_STATUS_UNKNOWN;
160 DistroRulesVersion installedDistroRulesVersion = null;
161 try {
162 installedDistroVersion = mInstaller.getInstalledDistroVersion();
163 if (installedDistroVersion == null) {
164 distroStatus = DISTRO_STATUS_NONE;
165 installedDistroRulesVersion = null;
166 } else {
167 distroStatus = DISTRO_STATUS_INSTALLED;
168 installedDistroRulesVersion = new DistroRulesVersion(
169 installedDistroVersion.rulesVersion,
170 installedDistroVersion.revision);
171 }
172 } catch (DistroException | IOException e) {
173 Slog.w(TAG, "Failed to read installed distro.", e);
174 }
175
Neil Fuller68f66662017-03-16 18:32:21 +0000176 boolean operationInProgress = this.mOperationInProgress.get();
177
178 // Determine the staged operation status, if possible.
179 DistroRulesVersion stagedDistroRulesVersion = null;
Neil Fuller87b11282017-06-23 16:43:45 +0100180 int stagedOperationStatus = STAGED_OPERATION_UNKNOWN;
Neil Fuller68f66662017-03-16 18:32:21 +0000181 if (!operationInProgress) {
182 StagedDistroOperation stagedDistroOperation;
183 try {
184 stagedDistroOperation = mInstaller.getStagedDistroOperation();
185 if (stagedDistroOperation == null) {
Neil Fuller87b11282017-06-23 16:43:45 +0100186 stagedOperationStatus = STAGED_OPERATION_NONE;
Neil Fuller68f66662017-03-16 18:32:21 +0000187 } else if (stagedDistroOperation.isUninstall) {
Neil Fuller87b11282017-06-23 16:43:45 +0100188 stagedOperationStatus = STAGED_OPERATION_UNINSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000189 } else {
190 // Must be an install.
Neil Fuller87b11282017-06-23 16:43:45 +0100191 stagedOperationStatus = STAGED_OPERATION_INSTALL;
Neil Fuller68f66662017-03-16 18:32:21 +0000192 DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion;
193 stagedDistroRulesVersion = new DistroRulesVersion(
194 stagedDistroVersion.rulesVersion,
195 stagedDistroVersion.revision);
196 }
197 } catch (DistroException | IOException e) {
198 Slog.w(TAG, "Failed to read staged distro.", e);
199 }
200 }
Neil Fullera1ae0252019-02-18 10:49:15 +0000201 return new RulesState(baseVersion.rulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
Neil Fuller68f66662017-03-16 18:32:21 +0000202 operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
203 distroStatus, installedDistroRulesVersion);
204 }
205 }
206
207 @Override
Neil Fuller54525bf2017-06-22 14:10:29 +0100208 public int requestInstall(ParcelFileDescriptor distroParcelFileDescriptor,
209 byte[] checkTokenBytes, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000210
Neil Fuller54525bf2017-06-22 14:10:29 +0100211 boolean closeParcelFileDescriptorOnExit = true;
212 try {
213 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
Neil Fuller68f66662017-03-16 18:32:21 +0000214
Neil Fuller54525bf2017-06-22 14:10:29 +0100215 CheckToken checkToken = null;
216 if (checkTokenBytes != null) {
217 checkToken = createCheckTokenOrThrow(checkTokenBytes);
218 }
Neil Fullerd857f672017-07-20 11:00:35 +0100219 EventLogTags.writeTimezoneRequestInstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000220
Neil Fuller54525bf2017-06-22 14:10:29 +0100221 synchronized (this) {
222 if (distroParcelFileDescriptor == null) {
223 throw new NullPointerException("distroParcelFileDescriptor == null");
224 }
225 if (callback == null) {
226 throw new NullPointerException("observer == null");
227 }
228 if (mOperationInProgress.get()) {
229 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
230 }
231 mOperationInProgress.set(true);
232
233 // Execute the install asynchronously.
234 mExecutor.execute(
235 new InstallRunnable(distroParcelFileDescriptor, checkToken, callback));
236
237 // The InstallRunnable now owns the ParcelFileDescriptor, so it will close it after
238 // it executes (and we do not have to).
239 closeParcelFileDescriptorOnExit = false;
240
241 return RulesManager.SUCCESS;
242 }
243 } finally {
244 // We should close() the local ParcelFileDescriptor we were passed if it hasn't been
245 // passed to another thread to handle.
246 if (distroParcelFileDescriptor != null && closeParcelFileDescriptorOnExit) {
247 try {
248 distroParcelFileDescriptor.close();
249 } catch (IOException e) {
250 Slog.w(TAG, "Failed to close distroParcelFileDescriptor", e);
251 }
252 }
Neil Fuller68f66662017-03-16 18:32:21 +0000253 }
254 }
255
256 private class InstallRunnable implements Runnable {
257
Neil Fuller54525bf2017-06-22 14:10:29 +0100258 private final ParcelFileDescriptor mDistroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000259 private final CheckToken mCheckToken;
260 private final ICallback mCallback;
261
Neil Fuller54525bf2017-06-22 14:10:29 +0100262 InstallRunnable(ParcelFileDescriptor distroParcelFileDescriptor, CheckToken checkToken,
263 ICallback callback) {
264 mDistroParcelFileDescriptor = distroParcelFileDescriptor;
Neil Fuller68f66662017-03-16 18:32:21 +0000265 mCheckToken = checkToken;
266 mCallback = callback;
267 }
268
269 @Override
270 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100271 EventLogTags.writeTimezoneInstallStarted(toStringOrNull(mCheckToken));
272
Neil Fuller54525bf2017-06-22 14:10:29 +0100273 boolean success = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000274 // Adopt the ParcelFileDescriptor into this try-with-resources so it is closed
275 // when we are done.
Neil Fuller54525bf2017-06-22 14:10:29 +0100276 try (ParcelFileDescriptor pfd = mDistroParcelFileDescriptor) {
277 // The ParcelFileDescriptor owns the underlying FileDescriptor and we'll close
278 // it at the end of the try-with-resources.
279 final boolean isFdOwner = false;
280 InputStream is = new FileInputStream(pfd.getFileDescriptor(), isFdOwner);
281
282 TimeZoneDistro distro = new TimeZoneDistro(is);
Neil Fullerfe3b1182017-06-19 12:56:08 +0100283 int installerResult = mInstaller.stageInstallWithErrorCode(distro);
Neil Fullerb1442272017-12-18 15:59:50 +0000284
285 // Notify interested parties that something is staged.
286 sendInstallNotificationIntentIfRequired(installerResult);
287
Neil Fuller68f66662017-03-16 18:32:21 +0000288 int resultCode = mapInstallerResultToApiCode(installerResult);
Neil Fullerd857f672017-07-20 11:00:35 +0100289 EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000290 sendFinishedStatus(mCallback, resultCode);
291
292 // All the installer failure modes are currently non-recoverable and won't be
293 // improved by trying again. Therefore success = true.
294 success = true;
295 } catch (Exception e) {
296 Slog.w(TAG, "Failed to install distro.", e);
Neil Fullerd857f672017-07-20 11:00:35 +0100297 EventLogTags.writeTimezoneInstallComplete(
298 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000299 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
300 } finally {
301 // Notify the package tracker that the operation is now complete.
302 mPackageTracker.recordCheckResult(mCheckToken, success);
303
304 mOperationInProgress.set(false);
305 }
306 }
307
Neil Fullerb1442272017-12-18 15:59:50 +0000308 private void sendInstallNotificationIntentIfRequired(int installerResult) {
309 if (installerResult == TimeZoneDistroInstaller.INSTALL_SUCCESS) {
310 mIntentHelper.sendTimeZoneOperationStaged();
311 }
312 }
313
Neil Fuller68f66662017-03-16 18:32:21 +0000314 private int mapInstallerResultToApiCode(int installerResult) {
315 switch (installerResult) {
316 case TimeZoneDistroInstaller.INSTALL_SUCCESS:
317 return Callback.SUCCESS;
318 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE:
319 return Callback.ERROR_INSTALL_BAD_DISTRO_STRUCTURE;
320 case TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD:
321 return Callback.ERROR_INSTALL_RULES_TOO_OLD;
322 case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION:
323 return Callback.ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION;
324 case TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR:
325 return Callback.ERROR_INSTALL_VALIDATION_ERROR;
326 default:
327 return Callback.ERROR_UNKNOWN_FAILURE;
328 }
329 }
330 }
331
332 @Override
333 public int requestUninstall(byte[] checkTokenBytes, ICallback callback) {
334 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
335
336 CheckToken checkToken = null;
337 if (checkTokenBytes != null) {
338 checkToken = createCheckTokenOrThrow(checkTokenBytes);
339 }
Neil Fullerd857f672017-07-20 11:00:35 +0100340 EventLogTags.writeTimezoneRequestUninstall(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000341 synchronized(this) {
342 if (callback == null) {
343 throw new NullPointerException("callback == null");
344 }
345
346 if (mOperationInProgress.get()) {
347 return RulesManager.ERROR_OPERATION_IN_PROGRESS;
348 }
349 mOperationInProgress.set(true);
350
351 // Execute the uninstall asynchronously.
352 mExecutor.execute(new UninstallRunnable(checkToken, callback));
353
354 return RulesManager.SUCCESS;
355 }
356 }
357
358 private class UninstallRunnable implements Runnable {
359
360 private final CheckToken mCheckToken;
361 private final ICallback mCallback;
362
Neil Fullera47c3632017-07-26 13:56:25 +0100363 UninstallRunnable(CheckToken checkToken, ICallback callback) {
Neil Fuller68f66662017-03-16 18:32:21 +0000364 mCheckToken = checkToken;
365 mCallback = callback;
366 }
367
368 @Override
369 public void run() {
Neil Fullerd857f672017-07-20 11:00:35 +0100370 EventLogTags.writeTimezoneUninstallStarted(toStringOrNull(mCheckToken));
Neil Fuller8e27c922017-09-14 09:34:56 +0100371 boolean packageTrackerStatus = false;
Neil Fuller68f66662017-03-16 18:32:21 +0000372 try {
Neil Fuller8e27c922017-09-14 09:34:56 +0100373 int uninstallResult = mInstaller.stageUninstall();
Neil Fullerb1442272017-12-18 15:59:50 +0000374
375 // Notify interested parties that something is staged.
376 sendUninstallNotificationIntentIfRequired(uninstallResult);
377
Neil Fuller8e27c922017-09-14 09:34:56 +0100378 packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS
379 || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
380
381 // Right now we just have Callback.SUCCESS / Callback.ERROR_UNKNOWN_FAILURE for
382 // uninstall. All clients should be checking against SUCCESS. More granular failures
383 // may be added in future.
384 int callbackResultCode =
385 packageTrackerStatus ? Callback.SUCCESS : Callback.ERROR_UNKNOWN_FAILURE;
Neil Fullerd857f672017-07-20 11:00:35 +0100386 EventLogTags.writeTimezoneUninstallComplete(
Neil Fuller8e27c922017-09-14 09:34:56 +0100387 toStringOrNull(mCheckToken), callbackResultCode);
388 sendFinishedStatus(mCallback, callbackResultCode);
Neil Fuller68f66662017-03-16 18:32:21 +0000389 } catch (Exception e) {
Neil Fullerd857f672017-07-20 11:00:35 +0100390 EventLogTags.writeTimezoneUninstallComplete(
391 toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
Neil Fuller68f66662017-03-16 18:32:21 +0000392 Slog.w(TAG, "Failed to uninstall distro.", e);
393 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
394 } finally {
395 // Notify the package tracker that the operation is now complete.
Neil Fuller8e27c922017-09-14 09:34:56 +0100396 mPackageTracker.recordCheckResult(mCheckToken, packageTrackerStatus);
Neil Fuller68f66662017-03-16 18:32:21 +0000397
398 mOperationInProgress.set(false);
399 }
400 }
Neil Fullerb1442272017-12-18 15:59:50 +0000401
402 private void sendUninstallNotificationIntentIfRequired(int uninstallResult) {
403 switch (uninstallResult) {
404 case TimeZoneDistroInstaller.UNINSTALL_SUCCESS:
405 mIntentHelper.sendTimeZoneOperationStaged();
406 break;
407 case TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED:
408 mIntentHelper.sendTimeZoneOperationUnstaged();
409 break;
410 case TimeZoneDistroInstaller.UNINSTALL_FAIL:
411 default:
412 // No-op - unknown or nothing to notify about.
413 }
414 }
Neil Fuller68f66662017-03-16 18:32:21 +0000415 }
416
417 private void sendFinishedStatus(ICallback callback, int resultCode) {
418 try {
419 callback.onFinished(resultCode);
420 } catch (RemoteException e) {
421 Slog.e(TAG, "Unable to notify observer of result", e);
422 }
423 }
424
425 @Override
426 public void requestNothing(byte[] checkTokenBytes, boolean success) {
427 mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
428 CheckToken checkToken = null;
429 if (checkTokenBytes != null) {
430 checkToken = createCheckTokenOrThrow(checkTokenBytes);
431 }
Neil Fullerd857f672017-07-20 11:00:35 +0100432 EventLogTags.writeTimezoneRequestNothing(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000433 mPackageTracker.recordCheckResult(checkToken, success);
Neil Fullerd857f672017-07-20 11:00:35 +0100434 EventLogTags.writeTimezoneNothingComplete(toStringOrNull(checkToken));
Neil Fuller68f66662017-03-16 18:32:21 +0000435 }
436
Neil Fuller87b11282017-06-23 16:43:45 +0100437 @Override
438 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
439 if (!mPermissionHelper.checkDumpPermission(TAG, pw)) {
440 return;
441 }
442
443 RulesState rulesState = getRulesStateInternal();
444 if (args != null && args.length == 2) {
445 // Formatting options used for automated tests. The format is less free-form than
446 // the -format options, which are intended to be easier to parse.
447 if ("-format_state".equals(args[0]) && args[1] != null) {
448 for (char c : args[1].toCharArray()) {
449 switch (c) {
Neil Fullera47c3632017-07-26 13:56:25 +0100450 case 'p': {
451 // Report operation in progress
452 String value = "Unknown";
453 if (rulesState != null) {
454 value = Boolean.toString(rulesState.isOperationInProgress());
Neil Fuller87b11282017-06-23 16:43:45 +0100455 }
Neil Fullera47c3632017-07-26 13:56:25 +0100456 pw.println("Operation in progress: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100457 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100458 }
Neil Fullera1ae0252019-02-18 10:49:15 +0000459 case 'b': {
460 // Report base rules version
Neil Fullera47c3632017-07-26 13:56:25 +0100461 String value = "Unknown";
462 if (rulesState != null) {
Neil Fullera1ae0252019-02-18 10:49:15 +0000463 value = rulesState.getBaseRulesVersion();
Neil Fullera47c3632017-07-26 13:56:25 +0100464 }
Neil Fullera1ae0252019-02-18 10:49:15 +0000465 pw.println("Base rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100466 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100467 }
468 case 'c': {
469 // Report current installation state
470 String value = "Unknown";
471 if (rulesState != null) {
472 value = distroStatusToString(rulesState.getDistroStatus());
473 }
474 pw.println("Current install state: " + value);
475 break;
476 }
477 case 'i': {
478 // Report currently installed version
479 String value = "Unknown";
480 if (rulesState != null) {
481 DistroRulesVersion installedRulesVersion =
482 rulesState.getInstalledDistroRulesVersion();
483 if (installedRulesVersion == null) {
484 value = "<None>";
485 } else {
486 value = installedRulesVersion.toDumpString();
487 }
488 }
489 pw.println("Installed rules version: " + value);
490 break;
491 }
492 case 'o': {
493 // Report staged operation type
494 String value = "Unknown";
495 if (rulesState != null) {
496 int stagedOperationType = rulesState.getStagedOperationType();
497 value = stagedOperationToString(stagedOperationType);
498 }
499 pw.println("Staged operation: " + value);
500 break;
501 }
502 case 't': {
Neil Fuller87b11282017-06-23 16:43:45 +0100503 // Report staged version (i.e. the one that will be installed next boot
504 // if the staged operation is an install).
Neil Fullera47c3632017-07-26 13:56:25 +0100505 String value = "Unknown";
506 if (rulesState != null) {
507 DistroRulesVersion stagedDistroRulesVersion =
508 rulesState.getStagedDistroRulesVersion();
509 if (stagedDistroRulesVersion == null) {
510 value = "<None>";
511 } else {
512 value = stagedDistroRulesVersion.toDumpString();
513 }
Neil Fuller87b11282017-06-23 16:43:45 +0100514 }
Neil Fullera47c3632017-07-26 13:56:25 +0100515 pw.println("Staged rules version: " + value);
Neil Fuller87b11282017-06-23 16:43:45 +0100516 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100517 }
518 case 'a': {
Neil Fuller87b11282017-06-23 16:43:45 +0100519 // Report the active rules version (i.e. the rules in use by the current
520 // process).
Neil Fullerc9036142017-10-04 17:04:38 +0100521 pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
Neil Fuller87b11282017-06-23 16:43:45 +0100522 + ICU.getTZDataVersion() + ","
Neil Fullerc9036142017-10-04 17:04:38 +0100523 + ZoneInfoDB.getInstance().getVersion() + ","
524 + TimeZoneFinder.getInstance().getIanaVersion());
Neil Fuller87b11282017-06-23 16:43:45 +0100525 break;
Neil Fullera47c3632017-07-26 13:56:25 +0100526 }
527 default: {
Neil Fuller87b11282017-06-23 16:43:45 +0100528 pw.println("Unknown option: " + c);
Neil Fullera47c3632017-07-26 13:56:25 +0100529 }
Neil Fuller87b11282017-06-23 16:43:45 +0100530 }
531 }
532 return;
533 }
534 }
535
536 pw.println("RulesManagerService state: " + toString());
Neil Fullerc9036142017-10-04 17:04:38 +0100537 pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
538 + ICU.getTZDataVersion() + ","
539 + ZoneInfoDB.getInstance().getVersion() + ","
540 + TimeZoneFinder.getInstance().getIanaVersion());
Neil Fullerd857f672017-07-20 11:00:35 +0100541 pw.println("Distro state: " + rulesState.toString());
Neil Fuller87b11282017-06-23 16:43:45 +0100542 mPackageTracker.dump(pw);
543 }
544
Neil Fullercd1a1092017-09-13 21:59:59 +0100545 /**
546 * Called when the device is considered idle.
547 */
548 void notifyIdle() {
549 // No package has changed: we are just triggering because the device is idle and there
550 // *might* be work to do.
551 final boolean packageChanged = false;
552 mPackageTracker.triggerUpdateIfNeeded(packageChanged);
553 }
554
Neil Fuller87b11282017-06-23 16:43:45 +0100555 @Override
556 public String toString() {
557 return "RulesManagerService{" +
558 "mOperationInProgress=" + mOperationInProgress +
559 '}';
560 }
561
Neil Fuller68f66662017-03-16 18:32:21 +0000562 private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) {
563 CheckToken checkToken;
564 try {
565 checkToken = CheckToken.fromByteArray(checkTokenBytes);
566 } catch (IOException e) {
567 throw new IllegalArgumentException("Unable to read token bytes "
568 + Arrays.toString(checkTokenBytes), e);
569 }
570 return checkToken;
571 }
Neil Fuller87b11282017-06-23 16:43:45 +0100572
573 private static String distroStatusToString(int distroStatus) {
574 switch(distroStatus) {
575 case DISTRO_STATUS_NONE:
576 return "None";
577 case DISTRO_STATUS_INSTALLED:
578 return "Installed";
579 case DISTRO_STATUS_UNKNOWN:
580 default:
581 return "Unknown";
582 }
583 }
584
585 private static String stagedOperationToString(int stagedOperationType) {
586 switch(stagedOperationType) {
587 case STAGED_OPERATION_NONE:
588 return "None";
589 case STAGED_OPERATION_UNINSTALL:
590 return "Uninstall";
591 case STAGED_OPERATION_INSTALL:
592 return "Install";
593 case STAGED_OPERATION_UNKNOWN:
594 default:
595 return "Unknown";
596 }
597 }
Neil Fullerd857f672017-07-20 11:00:35 +0100598
599 private static String toStringOrNull(Object obj) {
600 return obj == null ? null : obj.toString();
601 }
Neil Fuller68f66662017-03-16 18:32:21 +0000602}