168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller/*
268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller * Copyright (C) 2017 The Android Open Source Project
368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller *
468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller * you may not use this file except in compliance with the License.
668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller * You may obtain a copy of the License at
768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller *
868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller *
1068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller * Unless required by applicable law or agreed to in writing, software
1168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
1268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller * See the License for the specific language governing permissions and
1468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller * limitations under the License.
1568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller */
1668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
1768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerpackage com.android.server.timezone;
1868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
1968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport com.android.internal.annotations.VisibleForTesting;
20d857f676744af55c79c4871c881bf9598f6b21e9Neil Fullerimport com.android.server.EventLogTags;
2168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport com.android.server.SystemService;
22a6a71d0cd57d9f01605d0177032cd6bd1f4555f7Neil Fullerimport com.android.timezone.distro.DistroException;
23a6a71d0cd57d9f01605d0177032cd6bd1f4555f7Neil Fullerimport com.android.timezone.distro.DistroVersion;
24a6a71d0cd57d9f01605d0177032cd6bd1f4555f7Neil Fullerimport com.android.timezone.distro.StagedDistroOperation;
2554525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fullerimport com.android.timezone.distro.TimeZoneDistro;
265ca8b5bf77acb2e184ff374e9e028cfc2f168eaeNeil Fullerimport com.android.timezone.distro.installer.TimeZoneDistroInstaller;
2768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
2868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport android.app.timezone.Callback;
2968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport android.app.timezone.DistroFormatVersion;
3068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport android.app.timezone.DistroRulesVersion;
3168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport android.app.timezone.ICallback;
3268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport android.app.timezone.IRulesManager;
3368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport android.app.timezone.RulesManager;
3468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport android.app.timezone.RulesState;
3568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport android.content.Context;
3668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport android.os.ParcelFileDescriptor;
3768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport android.os.RemoteException;
3868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport android.util.Slog;
3968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
4068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport java.io.File;
4187b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fullerimport java.io.FileDescriptor;
4254525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fullerimport java.io.FileInputStream;
4368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport java.io.IOException;
4454525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fullerimport java.io.InputStream;
4587b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fullerimport java.io.PrintWriter;
4668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport java.util.Arrays;
4768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport java.util.concurrent.Executor;
4868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerimport java.util.concurrent.atomic.AtomicBoolean;
4987b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fullerimport libcore.icu.ICU;
50c90361404a1560d60756e4199af04d25ef688e2cNeil Fullerimport libcore.util.TimeZoneFinder;
5187b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fullerimport libcore.util.ZoneInfoDB;
5287b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller
5387b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fullerimport static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
5487b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fullerimport static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
5587b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fullerimport static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN;
5687b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fullerimport static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL;
5787b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fullerimport static android.app.timezone.RulesState.STAGED_OPERATION_NONE;
5887b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fullerimport static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL;
5987b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fullerimport static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN;
6068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
6168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fullerpublic final class RulesManagerService extends IRulesManager.Stub {
6268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
6368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private static final String TAG = "timezone.RulesManagerService";
6468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
6568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    /** The distro format supported by this device. */
6668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
6768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    static final DistroFormatVersion DISTRO_FORMAT_VERSION_SUPPORTED =
6868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            new DistroFormatVersion(
6968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    DistroVersion.CURRENT_FORMAT_MAJOR_VERSION,
7068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    DistroVersion.CURRENT_FORMAT_MINOR_VERSION);
7168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
7268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    public static class Lifecycle extends SystemService {
7368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        public Lifecycle(Context context) {
7468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            super(context);
7568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
7668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
7768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        @Override
7868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        public void onStart() {
79cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller            RulesManagerService service = RulesManagerService.create(getContext());
80cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller            service.start();
81cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller
82cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller            // Publish the binder service so it can be accessed from other (appropriately
83cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller            // permissioned) processes.
84cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller            publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, service);
8568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
86cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller            // Publish the service instance locally so we can use it directly from within the system
87cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller            // server from TimeZoneUpdateIdler.
88cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller            publishLocalService(RulesManagerService.class, service);
8968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
9068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
9168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
9268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
9368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    static final String REQUIRED_UPDATER_PERMISSION =
9468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            android.Manifest.permission.UPDATE_TIME_ZONE_RULES;
95b214bc44f1942b957f60dad75517e07cede77f18Neil Fuller    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
96b214bc44f1942b957f60dad75517e07cede77f18Neil Fuller    static final String REQUIRED_QUERY_PERMISSION =
97b214bc44f1942b957f60dad75517e07cede77f18Neil Fuller            android.Manifest.permission.QUERY_TIME_ZONE_RULES;
9868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata");
9968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
10068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
10168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private final AtomicBoolean mOperationInProgress = new AtomicBoolean(false);
10268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private final PermissionHelper mPermissionHelper;
10368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private final PackageTracker mPackageTracker;
10468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private final Executor mExecutor;
105b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller    private final RulesManagerIntentHelper mIntentHelper;
10668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private final TimeZoneDistroInstaller mInstaller;
10768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
10868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private static RulesManagerService create(Context context) {
10968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context);
11068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        return new RulesManagerService(
11168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                helper /* permissionHelper */,
11268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                helper /* executor */,
113b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                helper /* intentHelper */,
11468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                PackageTracker.create(context),
11568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR));
11668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
11768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
11868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    // A constructor that can be used by tests to supply mocked / faked dependencies.
119b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
120b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller    RulesManagerService(PermissionHelper permissionHelper, Executor executor,
121b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller            RulesManagerIntentHelper intentHelper, PackageTracker packageTracker,
12268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            TimeZoneDistroInstaller timeZoneDistroInstaller) {
12368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        mPermissionHelper = permissionHelper;
12468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        mExecutor = executor;
125b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller        mIntentHelper = intentHelper;
12668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        mPackageTracker = packageTracker;
12768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        mInstaller = timeZoneDistroInstaller;
12868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
12968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
13068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    public void start() {
1313582259c72476c55b60f49da1c7f1338b6683249Neil Fuller        // Return value deliberately ignored: no action required on failure to start.
13268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        mPackageTracker.start();
13368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
13468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
13568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    @Override // Binder call
13668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    public RulesState getRulesState() {
137b214bc44f1942b957f60dad75517e07cede77f18Neil Fuller        mPermissionHelper.enforceCallerHasPermission(REQUIRED_QUERY_PERMISSION);
13868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
13987b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        return getRulesStateInternal();
14087b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    }
14187b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller
14287b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    /** Like {@link #getRulesState()} without the permission check. */
14387b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    private RulesState getRulesStateInternal() {
14468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        synchronized(this) {
14568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            String systemRulesVersion;
14668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            try {
14768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                systemRulesVersion = mInstaller.getSystemRulesVersion();
14868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            } catch (IOException e) {
14968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                Slog.w(TAG, "Failed to read system rules", e);
15068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                return null;
15168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            }
15268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
153ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller            // Determine the installed distro state. This should be possible regardless of whether
154ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller            // there's an operation in progress.
155ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller            DistroVersion installedDistroVersion;
156ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller            int distroStatus = DISTRO_STATUS_UNKNOWN;
157ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller            DistroRulesVersion installedDistroRulesVersion = null;
158ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller            try {
159ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller                installedDistroVersion = mInstaller.getInstalledDistroVersion();
160ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller                if (installedDistroVersion == null) {
161ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller                    distroStatus = DISTRO_STATUS_NONE;
162ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller                    installedDistroRulesVersion = null;
163ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller                } else {
164ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller                    distroStatus = DISTRO_STATUS_INSTALLED;
165ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller                    installedDistroRulesVersion = new DistroRulesVersion(
166ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller                            installedDistroVersion.rulesVersion,
167ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller                            installedDistroVersion.revision);
168ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller                }
169ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller            } catch (DistroException | IOException e) {
170ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller                Slog.w(TAG, "Failed to read installed distro.", e);
171ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller            }
172ad3e1331f3311b0351856e29001610d6ff39f082Neil Fuller
17368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            boolean operationInProgress = this.mOperationInProgress.get();
17468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
17568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            // Determine the staged operation status, if possible.
17668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            DistroRulesVersion stagedDistroRulesVersion = null;
17787b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            int stagedOperationStatus = STAGED_OPERATION_UNKNOWN;
17868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            if (!operationInProgress) {
17968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                StagedDistroOperation stagedDistroOperation;
18068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                try {
18168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    stagedDistroOperation = mInstaller.getStagedDistroOperation();
18268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    if (stagedDistroOperation == null) {
18387b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                        stagedOperationStatus = STAGED_OPERATION_NONE;
18468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    } else if (stagedDistroOperation.isUninstall) {
18587b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                        stagedOperationStatus = STAGED_OPERATION_UNINSTALL;
18668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    } else {
18768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                        // Must be an install.
18887b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                        stagedOperationStatus = STAGED_OPERATION_INSTALL;
18968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                        DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion;
19068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                        stagedDistroRulesVersion = new DistroRulesVersion(
19168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                                stagedDistroVersion.rulesVersion,
19268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                                stagedDistroVersion.revision);
19368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    }
19468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                } catch (DistroException | IOException e) {
19568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    Slog.w(TAG, "Failed to read staged distro.", e);
19668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                }
19768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            }
19868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
19968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
20068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    distroStatus, installedDistroRulesVersion);
20168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
20268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
20368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
20468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    @Override
20554525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller    public int requestInstall(ParcelFileDescriptor distroParcelFileDescriptor,
20654525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            byte[] checkTokenBytes, ICallback callback) {
20768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
20854525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller        boolean closeParcelFileDescriptorOnExit = true;
20954525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller        try {
21054525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
21154525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller
21254525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            CheckToken checkToken = null;
21354525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            if (checkTokenBytes != null) {
21454525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                checkToken = createCheckTokenOrThrow(checkTokenBytes);
21568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            }
216d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller            EventLogTags.writeTimezoneRequestInstall(toStringOrNull(checkToken));
21768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
21854525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            synchronized (this) {
21954525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                if (distroParcelFileDescriptor == null) {
22054525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                    throw new NullPointerException("distroParcelFileDescriptor == null");
22154525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                }
22254525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                if (callback == null) {
22354525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                    throw new NullPointerException("observer == null");
22454525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                }
22554525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                if (mOperationInProgress.get()) {
22654525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                    return RulesManager.ERROR_OPERATION_IN_PROGRESS;
22754525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                }
22854525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                mOperationInProgress.set(true);
22954525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller
23054525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                // Execute the install asynchronously.
23154525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                mExecutor.execute(
23254525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                        new InstallRunnable(distroParcelFileDescriptor, checkToken, callback));
23354525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller
23454525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                // The InstallRunnable now owns the ParcelFileDescriptor, so it will close it after
23554525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                // it executes (and we do not have to).
23654525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                closeParcelFileDescriptorOnExit = false;
23768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
23854525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                return RulesManager.SUCCESS;
23954525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            }
24054525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller        } finally {
24154525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            // We should close() the local ParcelFileDescriptor we were passed if it hasn't been
24254525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            // passed to another thread to handle.
24354525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            if (distroParcelFileDescriptor != null && closeParcelFileDescriptorOnExit) {
24454525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                try {
24554525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                    distroParcelFileDescriptor.close();
24654525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                } catch (IOException e) {
24754525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                    Slog.w(TAG, "Failed to close distroParcelFileDescriptor", e);
24854525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                }
24954525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            }
25068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
25168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
25268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
25368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private class InstallRunnable implements Runnable {
25468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
25554525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller        private final ParcelFileDescriptor mDistroParcelFileDescriptor;
25668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        private final CheckToken mCheckToken;
25768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        private final ICallback mCallback;
25868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
25954525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller        InstallRunnable(ParcelFileDescriptor distroParcelFileDescriptor, CheckToken checkToken,
26054525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                ICallback callback) {
26154525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            mDistroParcelFileDescriptor = distroParcelFileDescriptor;
26268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            mCheckToken = checkToken;
26368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            mCallback = callback;
26468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
26568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
26668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        @Override
26768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        public void run() {
268d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller            EventLogTags.writeTimezoneInstallStarted(toStringOrNull(mCheckToken));
269d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller
27054525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            boolean success = false;
27168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            // Adopt the ParcelFileDescriptor into this try-with-resources so it is closed
27268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            // when we are done.
27354525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller            try (ParcelFileDescriptor pfd = mDistroParcelFileDescriptor) {
27454525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                // The ParcelFileDescriptor owns the underlying FileDescriptor and we'll close
27554525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                // it at the end of the try-with-resources.
27654525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                final boolean isFdOwner = false;
27754525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                InputStream is = new FileInputStream(pfd.getFileDescriptor(), isFdOwner);
27854525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller
27954525bf939ded4429aa3b7dd46954ed20bfa9320Neil Fuller                TimeZoneDistro distro = new TimeZoneDistro(is);
280fe3b1182c5e98018dca6a1093bd5983b9620b5a1Neil Fuller                int installerResult = mInstaller.stageInstallWithErrorCode(distro);
281b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller
282b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                // Notify interested parties that something is staged.
283b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                sendInstallNotificationIntentIfRequired(installerResult);
284b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller
28568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                int resultCode = mapInstallerResultToApiCode(installerResult);
286d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller                EventLogTags.writeTimezoneInstallComplete(toStringOrNull(mCheckToken), resultCode);
28768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                sendFinishedStatus(mCallback, resultCode);
28868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
28968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                // All the installer failure modes are currently non-recoverable and won't be
29068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                // improved by trying again. Therefore success = true.
29168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                success = true;
29268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            } catch (Exception e) {
29368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                Slog.w(TAG, "Failed to install distro.", e);
294d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller                EventLogTags.writeTimezoneInstallComplete(
295d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller                        toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
29668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
29768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            } finally {
29868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                // Notify the package tracker that the operation is now complete.
29968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                mPackageTracker.recordCheckResult(mCheckToken, success);
30068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
30168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                mOperationInProgress.set(false);
30268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            }
30368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
30468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
305b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller        private void sendInstallNotificationIntentIfRequired(int installerResult) {
306b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller            if (installerResult == TimeZoneDistroInstaller.INSTALL_SUCCESS) {
307b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                mIntentHelper.sendTimeZoneOperationStaged();
308b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller            }
309b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller        }
310b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller
31168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        private int mapInstallerResultToApiCode(int installerResult) {
31268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            switch (installerResult) {
31368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                case TimeZoneDistroInstaller.INSTALL_SUCCESS:
31468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    return Callback.SUCCESS;
31568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE:
31668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    return Callback.ERROR_INSTALL_BAD_DISTRO_STRUCTURE;
31768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                case TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD:
31868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    return Callback.ERROR_INSTALL_RULES_TOO_OLD;
31968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                case TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION:
32068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    return Callback.ERROR_INSTALL_BAD_DISTRO_FORMAT_VERSION;
32168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                case TimeZoneDistroInstaller.INSTALL_FAIL_VALIDATION_ERROR:
32268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    return Callback.ERROR_INSTALL_VALIDATION_ERROR;
32368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                default:
32468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    return Callback.ERROR_UNKNOWN_FAILURE;
32568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            }
32668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
32768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
32868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
32968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    @Override
33068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    public int requestUninstall(byte[] checkTokenBytes, ICallback callback) {
33168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
33268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
33368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        CheckToken checkToken = null;
33468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        if (checkTokenBytes != null) {
33568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            checkToken = createCheckTokenOrThrow(checkTokenBytes);
33668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
337d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller        EventLogTags.writeTimezoneRequestUninstall(toStringOrNull(checkToken));
33868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        synchronized(this) {
33968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            if (callback == null) {
34068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                throw new NullPointerException("callback == null");
34168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            }
34268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
34368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            if (mOperationInProgress.get()) {
34468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                return RulesManager.ERROR_OPERATION_IN_PROGRESS;
34568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            }
34668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            mOperationInProgress.set(true);
34768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
34868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            // Execute the uninstall asynchronously.
34968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            mExecutor.execute(new UninstallRunnable(checkToken, callback));
35068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
35168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            return RulesManager.SUCCESS;
35268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
35368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
35468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
35568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private class UninstallRunnable implements Runnable {
35668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
35768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        private final CheckToken mCheckToken;
35868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        private final ICallback mCallback;
35968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
360a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller        UninstallRunnable(CheckToken checkToken, ICallback callback) {
36168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            mCheckToken = checkToken;
36268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            mCallback = callback;
36368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
36468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
36568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        @Override
36668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        public void run() {
367d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller            EventLogTags.writeTimezoneUninstallStarted(toStringOrNull(mCheckToken));
3688e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller            boolean packageTrackerStatus = false;
36968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            try {
3708e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller                int uninstallResult = mInstaller.stageUninstall();
371b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller
372b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                // Notify interested parties that something is staged.
373b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                sendUninstallNotificationIntentIfRequired(uninstallResult);
374b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller
3758e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller                packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS
3768e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller                        || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
3778e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller
3788e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller                // Right now we just have Callback.SUCCESS / Callback.ERROR_UNKNOWN_FAILURE for
3798e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller                // uninstall. All clients should be checking against SUCCESS. More granular failures
3808e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller                // may be added in future.
3818e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller                int callbackResultCode =
3828e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller                        packageTrackerStatus ? Callback.SUCCESS : Callback.ERROR_UNKNOWN_FAILURE;
383d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller                EventLogTags.writeTimezoneUninstallComplete(
3848e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller                        toStringOrNull(mCheckToken), callbackResultCode);
3858e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller                sendFinishedStatus(mCallback, callbackResultCode);
38668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            } catch (Exception e) {
387d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller                EventLogTags.writeTimezoneUninstallComplete(
388d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller                        toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
38968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                Slog.w(TAG, "Failed to uninstall distro.", e);
39068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
39168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            } finally {
39268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                // Notify the package tracker that the operation is now complete.
3938e27c9226b82f41279ee6c108d2b06bb6f1cef5eNeil Fuller                mPackageTracker.recordCheckResult(mCheckToken, packageTrackerStatus);
39468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
39568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                mOperationInProgress.set(false);
39668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            }
39768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
398b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller
399b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller        private void sendUninstallNotificationIntentIfRequired(int uninstallResult) {
400b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller            switch (uninstallResult) {
401b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                case TimeZoneDistroInstaller.UNINSTALL_SUCCESS:
402b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                    mIntentHelper.sendTimeZoneOperationStaged();
403b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                    break;
404b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                case TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED:
405b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                    mIntentHelper.sendTimeZoneOperationUnstaged();
406b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                    break;
407b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                case TimeZoneDistroInstaller.UNINSTALL_FAIL:
408b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                default:
409b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller                    // No-op - unknown or nothing to notify about.
410b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller            }
411b144227742ada7fd693684a5bd2d53abd8e7c499Neil Fuller        }
41268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
41368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
41468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private void sendFinishedStatus(ICallback callback, int resultCode) {
41568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        try {
41668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            callback.onFinished(resultCode);
41768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        } catch (RemoteException e) {
41868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            Slog.e(TAG, "Unable to notify observer of result", e);
41968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
42068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
42168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
42268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    @Override
42368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    public void requestNothing(byte[] checkTokenBytes, boolean success) {
42468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
42568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        CheckToken checkToken = null;
42668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        if (checkTokenBytes != null) {
42768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            checkToken = createCheckTokenOrThrow(checkTokenBytes);
42868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
429d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller        EventLogTags.writeTimezoneRequestNothing(toStringOrNull(checkToken));
43068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        mPackageTracker.recordCheckResult(checkToken, success);
431d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller        EventLogTags.writeTimezoneNothingComplete(toStringOrNull(checkToken));
43268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
43368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller
43487b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    @Override
43587b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
43687b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        if (!mPermissionHelper.checkDumpPermission(TAG, pw)) {
43787b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            return;
43887b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        }
43987b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller
44087b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        RulesState rulesState = getRulesStateInternal();
44187b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        if (args != null && args.length == 2) {
44287b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            // Formatting options used for automated tests. The format is less free-form than
44387b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            // the -format options, which are intended to be easier to parse.
44487b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            if ("-format_state".equals(args[0]) && args[1] != null) {
44587b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                for (char c : args[1].toCharArray()) {
44687b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                    switch (c) {
447a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        case 'p': {
448a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            // Report operation in progress
449a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            String value = "Unknown";
450a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            if (rulesState != null) {
451a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                value = Boolean.toString(rulesState.isOperationInProgress());
452a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            }
453a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            pw.println("Operation in progress: " + value);
45487b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            break;
455a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        }
456a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        case 's': {
457a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            // Report system image rules version
458a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            String value = "Unknown";
459a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            if (rulesState != null) {
460a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                value = rulesState.getSystemRulesVersion();
461a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            }
462a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            pw.println("System rules version: " + value);
46387b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            break;
464a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        }
465a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        case 'c': {
466a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            // Report current installation state
467a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            String value = "Unknown";
468a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            if (rulesState != null) {
469a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                value = distroStatusToString(rulesState.getDistroStatus());
470a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            }
471a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            pw.println("Current install state: " + value);
47287b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            break;
473a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        }
474a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        case 'i': {
475a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            // Report currently installed version
476a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            String value = "Unknown";
477a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            if (rulesState != null) {
478a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                DistroRulesVersion installedRulesVersion =
479a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                        rulesState.getInstalledDistroRulesVersion();
480a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                if (installedRulesVersion == null) {
481a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                    value = "<None>";
482a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                } else {
483a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                    value = installedRulesVersion.toDumpString();
484a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                }
48587b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            }
486a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            pw.println("Installed rules version: " + value);
48787b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            break;
488a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        }
489a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        case 'o': {
490a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            // Report staged operation type
491a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            String value = "Unknown";
492a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            if (rulesState != null) {
493a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                int stagedOperationType = rulesState.getStagedOperationType();
494a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                value = stagedOperationToString(stagedOperationType);
495a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            }
496a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            pw.println("Staged operation: " + value);
49787b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            break;
498a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        }
499a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        case 't': {
50087b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            // Report staged version (i.e. the one that will be installed next boot
50187b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            // if the staged operation is an install).
502a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            String value = "Unknown";
503a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            if (rulesState != null) {
504a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                DistroRulesVersion stagedDistroRulesVersion =
505a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                        rulesState.getStagedDistroRulesVersion();
506a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                if (stagedDistroRulesVersion == null) {
507a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                    value = "<None>";
508a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                } else {
509a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                    value = stagedDistroRulesVersion.toDumpString();
510a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                                }
51187b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            }
512a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                            pw.println("Staged rules version: " + value);
51387b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            break;
514a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        }
515a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        case 'a': {
51687b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            // Report the active rules version (i.e. the rules in use by the current
51787b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            // process).
518c90361404a1560d60756e4199af04d25ef688e2cNeil Fuller                            pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
51987b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                                    + ICU.getTZDataVersion() + ","
520c90361404a1560d60756e4199af04d25ef688e2cNeil Fuller                                    + ZoneInfoDB.getInstance().getVersion() + ","
521c90361404a1560d60756e4199af04d25ef688e2cNeil Fuller                                    + TimeZoneFinder.getInstance().getIanaVersion());
52287b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            break;
523a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        }
524a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        default: {
52587b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                            pw.println("Unknown option: " + c);
526a47c3637f1af3481a094cdef3e20b6f2990589caNeil Fuller                        }
52787b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                    }
52887b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                }
52987b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                return;
53087b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            }
53187b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        }
53287b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller
53387b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        pw.println("RulesManagerService state: " + toString());
534c90361404a1560d60756e4199af04d25ef688e2cNeil Fuller        pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
535c90361404a1560d60756e4199af04d25ef688e2cNeil Fuller                + ICU.getTZDataVersion() + ","
536c90361404a1560d60756e4199af04d25ef688e2cNeil Fuller                + ZoneInfoDB.getInstance().getVersion() + ","
537c90361404a1560d60756e4199af04d25ef688e2cNeil Fuller                + TimeZoneFinder.getInstance().getIanaVersion());
538d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller        pw.println("Distro state: " + rulesState.toString());
53987b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        mPackageTracker.dump(pw);
54087b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    }
54187b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller
542cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller    /**
543cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller     * Called when the device is considered idle.
544cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller     */
545cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller    void notifyIdle() {
546cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller        // No package has changed: we are just triggering because the device is idle and there
547cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller        // *might* be work to do.
548cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller        final boolean packageChanged = false;
549cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller        mPackageTracker.triggerUpdateIfNeeded(packageChanged);
550cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller    }
551cd1a109b872f58f5baa0ce8438bf3ddd69eec9fcNeil Fuller
55287b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    @Override
55387b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    public String toString() {
55487b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        return "RulesManagerService{" +
55587b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                "mOperationInProgress=" + mOperationInProgress +
55687b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                '}';
55787b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    }
55887b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller
55968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) {
56068f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        CheckToken checkToken;
56168f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        try {
56268f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            checkToken = CheckToken.fromByteArray(checkTokenBytes);
56368f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        } catch (IOException e) {
56468f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller            throw new IllegalArgumentException("Unable to read token bytes "
56568f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller                    + Arrays.toString(checkTokenBytes), e);
56668f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        }
56768f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller        return checkToken;
56868f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller    }
56987b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller
57087b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    private static String distroStatusToString(int distroStatus) {
57187b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        switch(distroStatus) {
57287b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            case DISTRO_STATUS_NONE:
57387b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                return "None";
57487b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            case DISTRO_STATUS_INSTALLED:
57587b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                return "Installed";
57687b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            case DISTRO_STATUS_UNKNOWN:
57787b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            default:
57887b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                return "Unknown";
57987b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        }
58087b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    }
58187b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller
58287b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    private static String stagedOperationToString(int stagedOperationType) {
58387b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        switch(stagedOperationType) {
58487b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            case STAGED_OPERATION_NONE:
58587b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                return "None";
58687b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            case STAGED_OPERATION_UNINSTALL:
58787b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                return "Uninstall";
58887b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            case STAGED_OPERATION_INSTALL:
58987b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                return "Install";
59087b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            case STAGED_OPERATION_UNKNOWN:
59187b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller            default:
59287b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller                return "Unknown";
59387b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller        }
59487b1128ddc44afeafb946a32d63abef68d8b3dc1Neil Fuller    }
595d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller
596d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller    private static String toStringOrNull(Object obj) {
597d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller        return obj == null ? null : obj.toString();
598d857f676744af55c79c4871c881bf9598f6b21e9Neil Fuller    }
59968f666693a465eb8a66d9252b7b7ac035b9f0b7bNeil Fuller}
600