137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe/*
237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * Copyright (C) 2016 The Android Open Source Project
337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe *
437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * Licensed under the Apache License, Version 2.0 (the "License");
537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * you may not use this file except in compliance with the License.
637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * You may obtain a copy of the License at
737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe *
837e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe *      http://www.apache.org/licenses/LICENSE-2.0
937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe *
1037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * Unless required by applicable law or agreed to in writing, software
1137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * distributed under the License is distributed on an "AS IS" BASIS,
1237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * See the License for the specific language governing permissions and
1437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * limitations under the License.
1537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe */
1637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
1737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampepackage com.android.server.pm;
1837e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
1937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampeimport android.os.Environment;
2037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampeimport android.os.SystemClock;
2137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampeimport android.util.AtomicFile;
2237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
2337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampeimport java.io.File;
2437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampeimport java.util.concurrent.atomic.AtomicBoolean;
2537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampeimport java.util.concurrent.atomic.AtomicLong;
2637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
2737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe/**
2837e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * A simple base class for statistics that need to be saved/restored from a dedicated file. This
2937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * class provides a base implementation that:
3037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * <ul>
3137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * <li>Provide an AtomicFile to the actual read/write code
3237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * <li>A background-thread write and a synchronous write
3337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * <li>Write-limiting for the background-thread (by default writes are at least 30 minutes apart)
3437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * <li>Can lock on the provided data object before writing
3537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * </ul>
3637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * For completion, a subclass needs to implement actual {@link #writeInternal(Object) writing} and
3737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe * {@link #readInternal(Object) reading}.
3837e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe */
3937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampepublic abstract class AbstractStatsBase<T> {
4037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
4137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    private static final int WRITE_INTERVAL_MS =
4237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            (PackageManagerService.DEBUG_DEXOPT) ? 0 : 30*60*1000;
4337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    private final Object mFileLock = new Object();
4437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    private final AtomicLong mLastTimeWritten = new AtomicLong(0);
4537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false);
4637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    private final String mFileName;
4737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    private final String mBackgroundThreadName;
4837e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    private final boolean mLock;
4937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
5037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    protected AbstractStatsBase(String fileName, String threadName, boolean lock) {
5137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        mFileName = fileName;
5237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        mBackgroundThreadName = threadName;
5337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        mLock = lock;
5437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    }
5537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
5637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    protected AtomicFile getFile() {
5737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        File dataDir = Environment.getDataDirectory();
5837e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        File systemDir = new File(dataDir, "system");
5937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        File fname = new File(systemDir, mFileName);
6037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        return new AtomicFile(fname);
6137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    }
6237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
630318162abcbd07a0472989df43e00e353fac731bCalin Juravle    protected void writeNow(final T data) {
6437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        writeImpl(data);
6537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        mLastTimeWritten.set(SystemClock.elapsedRealtime());
6637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    }
6737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
680318162abcbd07a0472989df43e00e353fac731bCalin Juravle    protected boolean maybeWriteAsync(final T data) {
6937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        if (SystemClock.elapsedRealtime() - mLastTimeWritten.get() < WRITE_INTERVAL_MS
7037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            && !PackageManagerService.DEBUG_DEXOPT) {
7137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            return false;
7237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        }
7337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
7437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        if (mBackgroundWriteRunning.compareAndSet(false, true)) {
7537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            new Thread(mBackgroundThreadName) {
7637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                @Override
7737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                public void run() {
7837e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                    try {
7937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                        writeImpl(data);
8037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                        mLastTimeWritten.set(SystemClock.elapsedRealtime());
8137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                    } finally {
8237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                        mBackgroundWriteRunning.set(false);
8337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                    }
8437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                }
8537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            }.start();
8637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            return true;
8737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        }
8837e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
8937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        return false;
9037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    }
9137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
9237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    private void writeImpl(T data) {
9337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        if (mLock) {
9437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            synchronized (data) {
9537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                synchronized (mFileLock) {
9637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                    writeInternal(data);
9737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                }
9837e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            }
9937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        } else {
10037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            synchronized (mFileLock) {
10137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                writeInternal(data);
10237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            }
10337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        }
10437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    }
10537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
10637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    protected abstract void writeInternal(T data);
10737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
1080318162abcbd07a0472989df43e00e353fac731bCalin Juravle    protected void read(T data) {
10937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        if (mLock) {
11037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            synchronized (data) {
11137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                synchronized (mFileLock) {
11237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                    readInternal(data);
11337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                }
11437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            }
11537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        } else {
11637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            synchronized (mFileLock) {
11737e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe                readInternal(data);
11837e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe            }
11937e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        }
12037e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        // We use the current time as last-written. read() is called on system server startup
12137e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        // (current situation), and we want to postpone I/O at boot.
12237e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe        mLastTimeWritten.set(SystemClock.elapsedRealtime());
12337e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    }
12437e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe
12537e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe    protected abstract void readInternal(T data);
12637e5fdc6b4963f3533caecdd92b129f79da69dd8Andreas Gampe}
127