1f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller/*
2f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller * Copyright 2017 The Android Open Source Project
3f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller *
4f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
5f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller * you may not use this file except in compliance with the License.
6f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller * You may obtain a copy of the License at
7f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller *
8f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
9f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller *
10f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller * Unless required by applicable law or agreed to in writing, software
11f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
12f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller * See the License for the specific language governing permissions and
14f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller * limitations under the License.
15f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller */
16f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
17f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerpackage com.android.internal.telephony;
18f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
19f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerimport android.app.AlarmManager;
20f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerimport android.content.ContentResolver;
21f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerimport android.content.Context;
22f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerimport android.content.Intent;
23f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerimport android.database.ContentObserver;
24f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerimport android.os.Handler;
25f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerimport android.os.SystemClock;
26f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerimport android.os.SystemProperties;
27f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerimport android.os.UserHandle;
28f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerimport android.provider.Settings;
29f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
30f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller/**
31f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller * An interface to various time / time zone detection behaviors that should be centralized into a
32f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller * new service.
33f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller */
34f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller// Non-final to allow mocking.
35f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fullerpublic class TimeServiceHelper {
36f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
37f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    /**
38f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * Callback interface for automatic detection enable/disable changes.
39f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     */
40f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    public interface Listener {
41f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        /**
42f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller         * Automatic time detection has been enabled or disabled.
43f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller         */
44f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        void onTimeDetectionChange(boolean enabled);
45f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
46f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        /**
47f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller         * Automatic time zone detection has been enabled or disabled.
48f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller         */
49f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        void onTimeZoneDetectionChange(boolean enabled);
50f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    }
51f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
5292a32ccfd8f20d8cf3dee618e730fa73315331e4Neil Fuller    private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
53f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
54f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    private final Context mContext;
55f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    private final ContentResolver mCr;
56f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
57f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    private Listener mListener;
58f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
59f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    /** Creates a TimeServiceHelper */
60f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    public TimeServiceHelper(Context context) {
61f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        mContext = context;
62f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        mCr = context.getContentResolver();
63f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    }
64f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
65f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    /**
66f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * Sets a listener that will be called when the automatic time / time zone detection setting
67f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * changes.
68f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     */
69f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    public void setListener(Listener listener) {
70f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        if (listener == null) {
71f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller            throw new NullPointerException("listener==null");
72f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        }
73f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        if (mListener != null) {
74f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller            throw new IllegalStateException("listener already set");
75f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        }
76f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        this.mListener = listener;
77f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        mCr.registerContentObserver(
78f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
79f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                new ContentObserver(new Handler()) {
80f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                    public void onChange(boolean selfChange) {
81f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                        listener.onTimeDetectionChange(isTimeDetectionEnabled());
82f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                    }
83f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                });
84f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        mCr.registerContentObserver(
85f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
86f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                new ContentObserver(new Handler()) {
87f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                    public void onChange(boolean selfChange) {
88f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                        listener.onTimeZoneDetectionChange(isTimeZoneDetectionEnabled());
89f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                    }
90f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller                });
91f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    }
92f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
93f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    /**
947cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller     * Returns the same value as {@link System#currentTimeMillis()}.
957cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller     */
967cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller    public long currentTimeMillis() {
977cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller        return System.currentTimeMillis();
987cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller    }
997cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller
1007cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller    /**
1017cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller     * Returns the same value as {@link SystemClock#elapsedRealtime()}.
1027cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller     */
1037cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller    public long elapsedRealtime() {
1047cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller        return SystemClock.elapsedRealtime();
1057cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller    }
1067cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller
1077cecf024c258ae234dcaf124cc32ed0e6d0d929eNeil Fuller    /**
108f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * Returns true if the device has an explicit time zone set.
109f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     */
110f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    public boolean isTimeZoneSettingInitialized() {
111eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        return isTimeZoneSettingInitializedStatic();
11292a32ccfd8f20d8cf3dee618e730fa73315331e4Neil Fuller
113f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    }
114f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
115f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    /**
116f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * Returns true if automatic time detection is enabled in settings.
117f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     */
118f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    public boolean isTimeDetectionEnabled() {
119f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        try {
120f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller            return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME) > 0;
121f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        } catch (Settings.SettingNotFoundException snfe) {
122f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller            return true;
123f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        }
124f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    }
125f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
126f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    /**
127f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * Returns true if automatic time zone detection is enabled in settings.
128f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     */
129f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    public boolean isTimeZoneDetectionEnabled() {
130f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        try {
131f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller            return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE) > 0;
132f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        } catch (Settings.SettingNotFoundException snfe) {
133f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller            return true;
134f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        }
135f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    }
136f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
137f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    /**
138f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * Set the device time zone and send out a sticky broadcast so the system can
139f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * determine if the timezone was set by the carrier.
140f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     *
141f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * @param zoneId timezone set by carrier
142f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     */
143f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    public void setDeviceTimeZone(String zoneId) {
144eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        setDeviceTimeZoneStatic(mContext, zoneId);
145f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    }
146f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller
147f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    /**
148f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * Set the time and Send out a sticky broadcast so the system can determine
149f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * if the time was set by the carrier.
150f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     *
151f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     * @param time time set by network
152f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller     */
153f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    public void setDeviceTime(long time) {
154f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        SystemClock.setCurrentTimeMillis(time);
155f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
156f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
157f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        intent.putExtra("time", time);
158f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
159f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller    }
160eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller
161eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller    /**
162eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller     * Static implementation of isTimeZoneSettingInitialized() for use from {@link MccTable}. This
163eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller     * is a hack to deflake TelephonyTests when running on a device with a real SIM: in that
164eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller     * situation real service events may come in while a TelephonyTest is running, leading to flakes
165eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller     * as the real / fake instance of TimeServiceHelper is swapped in and out from
166eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller     * {@link TelephonyComponentFactory}.
167eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller     */
168eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller    static boolean isTimeZoneSettingInitializedStatic() {
169eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        // timezone.equals("GMT") will be true and only true if the timezone was
170eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        // set to a default value by the system server (when starting, system server
171eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        // sets the persist.sys.timezone to "GMT" if it's not set). "GMT" is not used by
172eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        // any code that sets it explicitly (in case where something sets GMT explicitly,
173eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        // "Etc/GMT" Olsen ID would be used).
174eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        // TODO(b/64056758): Remove "timezone.equals("GMT")" hack when there's a
175eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        // better way of telling if the value has been defaulted.
176eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller
177eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        String timeZoneId = SystemProperties.get(TIMEZONE_PROPERTY);
178eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        return timeZoneId != null && timeZoneId.length() > 0 && !timeZoneId.equals("GMT");
179eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller    }
180eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller
181eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller    /**
182eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller     * Static method for use by MccTable. See {@link #isTimeZoneSettingInitializedStatic()} for
183eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller     * explanation.
184eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller     */
185eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller    static void setDeviceTimeZoneStatic(Context context, String zoneId) {
186eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
187eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        alarmManager.setTimeZone(zoneId);
188eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
189eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
190eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        intent.putExtra("time-zone", zoneId);
191eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller        context.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
192eff964878d590a8a74837c48bbb0e4ff4ff8f28fNeil Fuller    }
193f9e4314c7258d07ee1b995d625d20887d36c94daNeil Fuller}
194