1 /* 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 17 package android.database.sqlite; 18 19 import android.annotation.TestApi; 20 import android.app.ActivityThread; 21 import android.app.Application; 22 import android.provider.Settings; 23 import android.text.TextUtils; 24 import android.util.KeyValueListParser; 25 import android.util.Log; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 29 /** 30 * Helper class for accessing 31 * {@link Settings.Global#SQLITE_COMPATIBILITY_WAL_FLAGS global compatibility WAL settings}. 32 * 33 * <p>The value of {@link Settings.Global#SQLITE_COMPATIBILITY_WAL_FLAGS} is cached on first access 34 * for consistent behavior across all connections opened in the process. 35 * @hide 36 */ 37 @TestApi 38 public class SQLiteCompatibilityWalFlags { 39 40 private static final String TAG = "SQLiteCompatibilityWalFlags"; 41 42 private static volatile boolean sInitialized; 43 private static volatile boolean sLegacyCompatibilityWalEnabled; 44 private static volatile String sWALSyncMode; 45 private static volatile long sTruncateSize = -1; 46 // This flag is used to avoid recursive initialization due to circular dependency on Settings 47 private static volatile boolean sCallingGlobalSettings; 48 SQLiteCompatibilityWalFlags()49 private SQLiteCompatibilityWalFlags() { 50 } 51 52 /** 53 * @hide 54 */ 55 @VisibleForTesting isLegacyCompatibilityWalEnabled()56 public static boolean isLegacyCompatibilityWalEnabled() { 57 initIfNeeded(); 58 return sLegacyCompatibilityWalEnabled; 59 } 60 61 /** 62 * @hide 63 */ 64 @VisibleForTesting getWALSyncMode()65 public static String getWALSyncMode() { 66 initIfNeeded(); 67 // The configurable WAL sync mode should only ever be used if the legacy compatibility 68 // WAL is enabled. It should *not* have any effect if app developers explicitly turn on 69 // WAL for their database using setWriteAheadLoggingEnabled. Throwing an exception here 70 // adds an extra layer of checking that we never use it in the wrong place. 71 if (!sLegacyCompatibilityWalEnabled) { 72 throw new IllegalStateException("isLegacyCompatibilityWalEnabled() == false"); 73 } 74 75 return sWALSyncMode; 76 } 77 78 /** 79 * Override {@link com.android.internal.R.integer#db_wal_truncate_size}. 80 * 81 * @return the value set in the global setting, or -1 if a value is not set. 82 * 83 * @hide 84 */ 85 @VisibleForTesting getTruncateSize()86 public static long getTruncateSize() { 87 initIfNeeded(); 88 return sTruncateSize; 89 } 90 initIfNeeded()91 private static void initIfNeeded() { 92 if (sInitialized || sCallingGlobalSettings) { 93 return; 94 } 95 ActivityThread activityThread = ActivityThread.currentActivityThread(); 96 Application app = activityThread == null ? null : activityThread.getApplication(); 97 String flags = null; 98 if (app == null) { 99 Log.w(TAG, "Cannot read global setting " 100 + Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS + " - " 101 + "Application state not available"); 102 } else { 103 try { 104 sCallingGlobalSettings = true; 105 flags = Settings.Global.getString(app.getContentResolver(), 106 Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS); 107 } finally { 108 sCallingGlobalSettings = false; 109 } 110 } 111 112 init(flags); 113 } 114 115 /** 116 * @hide 117 */ 118 @VisibleForTesting init(String flags)119 public static void init(String flags) { 120 if (TextUtils.isEmpty(flags)) { 121 sInitialized = true; 122 return; 123 } 124 KeyValueListParser parser = new KeyValueListParser(','); 125 try { 126 parser.setString(flags); 127 } catch (IllegalArgumentException e) { 128 Log.e(TAG, "Setting has invalid format: " + flags, e); 129 sInitialized = true; 130 return; 131 } 132 sLegacyCompatibilityWalEnabled = parser.getBoolean( 133 "legacy_compatibility_wal_enabled", false); 134 sWALSyncMode = parser.getString("wal_syncmode", SQLiteGlobal.getWALSyncMode()); 135 sTruncateSize = parser.getInt("truncate_size", -1); 136 Log.i(TAG, "Read compatibility WAL flags: legacy_compatibility_wal_enabled=" 137 + sLegacyCompatibilityWalEnabled + ", wal_syncmode=" + sWALSyncMode); 138 sInitialized = true; 139 } 140 141 /** 142 * @hide 143 */ 144 @VisibleForTesting 145 @TestApi reset()146 public static void reset() { 147 sInitialized = false; 148 sLegacyCompatibilityWalEnabled = false; 149 sWALSyncMode = null; 150 } 151 } 152