| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.server.net; |
| |
| import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; |
| import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; |
| import static android.net.INetd.FIREWALL_CHAIN_STANDBY; |
| import static android.net.INetd.FIREWALL_RULE_ALLOW; |
| import static android.net.INetd.FIREWALL_RULE_DENY; |
| import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; |
| import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; |
| import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; |
| import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; |
| |
| import android.app.ActivityManager; |
| import android.net.NetworkPolicyManager; |
| import android.util.Log; |
| import android.util.Slog; |
| |
| import com.android.internal.util.IndentingPrintWriter; |
| import com.android.internal.util.RingBuffer; |
| import com.android.server.am.ProcessList; |
| |
| import java.text.SimpleDateFormat; |
| import java.util.Arrays; |
| import java.util.Date; |
| import java.util.Set; |
| |
| public class NetworkPolicyLogger { |
| static final String TAG = "NetworkPolicy"; |
| |
| static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG); |
| static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE); |
| |
| private static final int MAX_LOG_SIZE = |
| ActivityManager.isLowRamDeviceStatic() ? 100 : 400; |
| private static final int MAX_NETWORK_BLOCKED_LOG_SIZE = |
| ActivityManager.isLowRamDeviceStatic() ? 100 : 400; |
| |
| private static final int EVENT_TYPE_GENERIC = 0; |
| private static final int EVENT_NETWORK_BLOCKED = 1; |
| private static final int EVENT_UID_STATE_CHANGED = 2; |
| private static final int EVENT_POLICIES_CHANGED = 3; |
| private static final int EVENT_METEREDNESS_CHANGED = 4; |
| private static final int EVENT_USER_STATE_REMOVED = 5; |
| private static final int EVENT_RESTRICT_BG_CHANGED = 6; |
| private static final int EVENT_DEVICE_IDLE_MODE_ENABLED = 7; |
| private static final int EVENT_APP_IDLE_STATE_CHANGED = 8; |
| private static final int EVENT_PAROLE_STATE_CHANGED = 9; |
| private static final int EVENT_TEMP_POWER_SAVE_WL_CHANGED = 10; |
| private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11; |
| private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12; |
| private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13; |
| private static final int EVENT_APP_IDLE_WL_CHANGED = 14; |
| |
| static final int NTWK_BLOCKED_POWER = 0; |
| static final int NTWK_ALLOWED_NON_METERED = 1; |
| static final int NTWK_BLOCKED_BLACKLIST = 2; |
| static final int NTWK_ALLOWED_WHITELIST = 3; |
| static final int NTWK_ALLOWED_TMP_WHITELIST = 4; |
| static final int NTWK_BLOCKED_BG_RESTRICT = 5; |
| static final int NTWK_ALLOWED_DEFAULT = 6; |
| static final int NTWK_ALLOWED_SYSTEM = 7; |
| |
| private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE); |
| private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE); |
| private final LogBuffer mEventsBuffer = new LogBuffer(MAX_LOG_SIZE); |
| |
| private final Object mLock = new Object(); |
| |
| void networkBlocked(int uid, int reason) { |
| synchronized (mLock) { |
| if (LOGD) Slog.d(TAG, uid + " is " + getBlockedReason(reason)); |
| mNetworkBlockedBuffer.networkBlocked(uid, reason); |
| } |
| } |
| |
| void uidStateChanged(int uid, int procState, long procStateSeq) { |
| synchronized (mLock) { |
| if (LOGV) Slog.v(TAG, |
| uid + " state changed to " + procState + " with seq=" + procStateSeq); |
| mUidStateChangeBuffer.uidStateChanged(uid, procState, procStateSeq); |
| } |
| } |
| |
| void event(String msg) { |
| synchronized (mLock) { |
| if (LOGV) Slog.v(TAG, msg); |
| mEventsBuffer.event(msg); |
| } |
| } |
| |
| void uidPolicyChanged(int uid, int oldPolicy, int newPolicy) { |
| synchronized (mLock) { |
| if (LOGV) Slog.v(TAG, getPolicyChangedLog(uid, oldPolicy, newPolicy)); |
| mEventsBuffer.uidPolicyChanged(uid, oldPolicy, newPolicy); |
| } |
| } |
| |
| void meterednessChanged(int netId, boolean newMetered) { |
| synchronized (mLock) { |
| if (LOGD) Slog.d(TAG, getMeterednessChangedLog(netId, newMetered)); |
| mEventsBuffer.meterednessChanged(netId, newMetered); |
| } |
| } |
| |
| void removingUserState(int userId) { |
| synchronized (mLock) { |
| if (LOGD) Slog.d(TAG, getUserRemovedLog(userId)); |
| mEventsBuffer.userRemoved(userId); |
| } |
| } |
| |
| void restrictBackgroundChanged(boolean oldValue, boolean newValue) { |
| synchronized (mLock) { |
| if (LOGD) Slog.d(TAG, |
| getRestrictBackgroundChangedLog(oldValue, newValue)); |
| mEventsBuffer.restrictBackgroundChanged(oldValue, newValue); |
| } |
| } |
| |
| void deviceIdleModeEnabled(boolean enabled) { |
| synchronized (mLock) { |
| if (LOGD) Slog.d(TAG, getDeviceIdleModeEnabled(enabled)); |
| mEventsBuffer.deviceIdleModeEnabled(enabled); |
| } |
| } |
| |
| void appIdleStateChanged(int uid, boolean idle) { |
| synchronized (mLock) { |
| if (LOGD) Slog.d(TAG, getAppIdleChangedLog(uid, idle)); |
| mEventsBuffer.appIdleStateChanged(uid, idle); |
| } |
| } |
| |
| void appIdleWlChanged(int uid, boolean isWhitelisted) { |
| synchronized (mLock) { |
| if (LOGD) Slog.d(TAG, getAppIdleWlChangedLog(uid, isWhitelisted)); |
| mEventsBuffer.appIdleWlChanged(uid, isWhitelisted); |
| } |
| } |
| |
| void paroleStateChanged(boolean paroleOn) { |
| synchronized (mLock) { |
| if (LOGD) Slog.d(TAG, getParoleStateChanged(paroleOn)); |
| mEventsBuffer.paroleStateChanged(paroleOn); |
| } |
| } |
| |
| void tempPowerSaveWlChanged(int appId, boolean added) { |
| synchronized (mLock) { |
| if (LOGV) Slog.v(TAG, getTempPowerSaveWlChangedLog(appId, added)); |
| mEventsBuffer.tempPowerSaveWlChanged(appId, added); |
| } |
| } |
| |
| void uidFirewallRuleChanged(int chain, int uid, int rule) { |
| synchronized (mLock) { |
| if (LOGV) Slog.v(TAG, getUidFirewallRuleChangedLog(chain, uid, rule)); |
| mEventsBuffer.uidFirewallRuleChanged(chain, uid, rule); |
| } |
| } |
| |
| void firewallChainEnabled(int chain, boolean enabled) { |
| synchronized (mLock) { |
| if (LOGD) Slog.d(TAG, getFirewallChainEnabledLog(chain, enabled)); |
| mEventsBuffer.firewallChainEnabled(chain, enabled); |
| } |
| } |
| |
| void firewallRulesChanged(int chain, int[] uids, int[] rules) { |
| synchronized (mLock) { |
| final String log = "Firewall rules changed for " + getFirewallChainName(chain) |
| + "; uids=" + Arrays.toString(uids) + "; rules=" + Arrays.toString(rules); |
| if (LOGD) Slog.d(TAG, log); |
| mEventsBuffer.event(log); |
| } |
| } |
| |
| void meteredRestrictedPkgsChanged(Set<Integer> restrictedUids) { |
| synchronized (mLock) { |
| final String log = "Metered restricted uids: " + restrictedUids; |
| if (LOGD) Slog.d(TAG, log); |
| mEventsBuffer.event(log); |
| } |
| } |
| |
| void dumpLogs(IndentingPrintWriter pw) { |
| synchronized (mLock) { |
| pw.println(); |
| pw.println("mEventLogs (most recent first):"); |
| pw.increaseIndent(); |
| mEventsBuffer.reverseDump(pw); |
| pw.decreaseIndent(); |
| |
| pw.println(); |
| pw.println("mNetworkBlockedLogs (most recent first):"); |
| pw.increaseIndent(); |
| mNetworkBlockedBuffer.reverseDump(pw); |
| pw.decreaseIndent(); |
| |
| pw.println(); |
| pw.println("mUidStateChangeLogs (most recent first):"); |
| pw.increaseIndent(); |
| mUidStateChangeBuffer.reverseDump(pw); |
| pw.decreaseIndent(); |
| } |
| } |
| |
| private static String getBlockedReason(int reason) { |
| switch (reason) { |
| case NTWK_BLOCKED_POWER: |
| return "blocked by power restrictions"; |
| case NTWK_ALLOWED_NON_METERED: |
| return "allowed on unmetered network"; |
| case NTWK_BLOCKED_BLACKLIST: |
| return "blacklisted on metered network"; |
| case NTWK_ALLOWED_WHITELIST: |
| return "whitelisted on metered network"; |
| case NTWK_ALLOWED_TMP_WHITELIST: |
| return "temporary whitelisted on metered network"; |
| case NTWK_BLOCKED_BG_RESTRICT: |
| return "blocked when background is restricted"; |
| case NTWK_ALLOWED_DEFAULT: |
| return "allowed by default"; |
| default: |
| return String.valueOf(reason); |
| } |
| } |
| |
| private static String getPolicyChangedLog(int uid, int oldPolicy, int newPolicy) { |
| return "Policy for " + uid + " changed from " |
| + NetworkPolicyManager.uidPoliciesToString(oldPolicy) + " to " |
| + NetworkPolicyManager.uidPoliciesToString(newPolicy); |
| } |
| |
| private static String getMeterednessChangedLog(int netId, boolean newMetered) { |
| return "Meteredness of netId=" + netId + " changed to " + newMetered; |
| } |
| |
| private static String getUserRemovedLog(int userId) { |
| return "Remove state for u" + userId; |
| } |
| |
| private static String getRestrictBackgroundChangedLog(boolean oldValue, boolean newValue) { |
| return "Changed restrictBackground: " + oldValue + "->" + newValue; |
| } |
| |
| private static String getDeviceIdleModeEnabled(boolean enabled) { |
| return "DeviceIdleMode enabled: " + enabled; |
| } |
| |
| private static String getAppIdleChangedLog(int uid, boolean idle) { |
| return "App idle state of uid " + uid + ": " + idle; |
| } |
| |
| private static String getAppIdleWlChangedLog(int uid, boolean isWhitelisted) { |
| return "App idle whitelist state of uid " + uid + ": " + isWhitelisted; |
| } |
| |
| private static String getParoleStateChanged(boolean paroleOn) { |
| return "Parole state: " + paroleOn; |
| } |
| |
| private static String getTempPowerSaveWlChangedLog(int appId, boolean added) { |
| return "temp-power-save whitelist for " + appId + " changed to: " + added; |
| } |
| |
| private static String getUidFirewallRuleChangedLog(int chain, int uid, int rule) { |
| return String.format("Firewall rule changed: %d-%s-%s", |
| uid, getFirewallChainName(chain), getFirewallRuleName(rule)); |
| } |
| |
| private static String getFirewallChainEnabledLog(int chain, boolean enabled) { |
| return "Firewall chain " + getFirewallChainName(chain) + " state: " + enabled; |
| } |
| |
| private static String getFirewallChainName(int chain) { |
| switch (chain) { |
| case FIREWALL_CHAIN_DOZABLE: |
| return FIREWALL_CHAIN_NAME_DOZABLE; |
| case FIREWALL_CHAIN_STANDBY: |
| return FIREWALL_CHAIN_NAME_STANDBY; |
| case FIREWALL_CHAIN_POWERSAVE: |
| return FIREWALL_CHAIN_NAME_POWERSAVE; |
| default: |
| return String.valueOf(chain); |
| } |
| } |
| |
| private static String getFirewallRuleName(int rule) { |
| switch (rule) { |
| case FIREWALL_RULE_DEFAULT: |
| return "default"; |
| case FIREWALL_RULE_ALLOW: |
| return "allow"; |
| case FIREWALL_RULE_DENY: |
| return "deny"; |
| default: |
| return String.valueOf(rule); |
| } |
| } |
| |
| private final static class LogBuffer extends RingBuffer<Data> { |
| private static final SimpleDateFormat sFormatter |
| = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSS"); |
| private static final Date sDate = new Date(); |
| |
| public LogBuffer(int capacity) { |
| super(Data.class, capacity); |
| } |
| |
| public void uidStateChanged(int uid, int procState, long procStateSeq) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_UID_STATE_CHANGED; |
| data.ifield1 = uid; |
| data.ifield2 = procState; |
| data.lfield1 = procStateSeq; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void event(String msg) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_TYPE_GENERIC; |
| data.sfield1 = msg; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void networkBlocked(int uid, int reason) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_NETWORK_BLOCKED; |
| data.ifield1 = uid; |
| data.ifield2 = reason; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void uidPolicyChanged(int uid, int oldPolicy, int newPolicy) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_POLICIES_CHANGED; |
| data.ifield1 = uid; |
| data.ifield2 = oldPolicy; |
| data.ifield3 = newPolicy; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void meterednessChanged(int netId, boolean newMetered) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_METEREDNESS_CHANGED; |
| data.ifield1 = netId; |
| data.bfield1 = newMetered; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void userRemoved(int userId) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_USER_STATE_REMOVED; |
| data.ifield1 = userId; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void restrictBackgroundChanged(boolean oldValue, boolean newValue) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_RESTRICT_BG_CHANGED; |
| data.bfield1 = oldValue; |
| data.bfield2 = newValue; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void deviceIdleModeEnabled(boolean enabled) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_DEVICE_IDLE_MODE_ENABLED; |
| data.bfield1 = enabled; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void appIdleStateChanged(int uid, boolean idle) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_APP_IDLE_STATE_CHANGED; |
| data.ifield1 = uid; |
| data.bfield1 = idle; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void appIdleWlChanged(int uid, boolean isWhitelisted) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_APP_IDLE_WL_CHANGED; |
| data.ifield1 = uid; |
| data.bfield1 = isWhitelisted; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void paroleStateChanged(boolean paroleOn) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_PAROLE_STATE_CHANGED; |
| data.bfield1 = paroleOn; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void tempPowerSaveWlChanged(int appId, boolean added) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_TEMP_POWER_SAVE_WL_CHANGED; |
| data.ifield1 = appId; |
| data.bfield1 = added; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void uidFirewallRuleChanged(int chain, int uid, int rule) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_UID_FIREWALL_RULE_CHANGED; |
| data.ifield1 = chain; |
| data.ifield2 = uid; |
| data.ifield3 = rule; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void firewallChainEnabled(int chain, boolean enabled) { |
| final Data data = getNextSlot(); |
| if (data == null) return; |
| |
| data.reset(); |
| data.type = EVENT_FIREWALL_CHAIN_ENABLED; |
| data.ifield1 = chain; |
| data.bfield1 = enabled; |
| data.timeStamp = System.currentTimeMillis(); |
| } |
| |
| public void reverseDump(IndentingPrintWriter pw) { |
| final Data[] allData = toArray(); |
| for (int i = allData.length - 1; i >= 0; --i) { |
| if (allData[i] == null) { |
| pw.println("NULL"); |
| continue; |
| } |
| pw.print(formatDate(allData[i].timeStamp)); |
| pw.print(" - "); |
| pw.println(getContent(allData[i])); |
| } |
| } |
| |
| public String getContent(Data data) { |
| switch (data.type) { |
| case EVENT_TYPE_GENERIC: |
| return data.sfield1; |
| case EVENT_NETWORK_BLOCKED: |
| return data.ifield1 + "-" + getBlockedReason(data.ifield2); |
| case EVENT_UID_STATE_CHANGED: |
| return data.ifield1 + "-" + ProcessList.makeProcStateString(data.ifield2) |
| + "-" + data.lfield1; |
| case EVENT_POLICIES_CHANGED: |
| return getPolicyChangedLog(data.ifield1, data.ifield2, data.ifield3); |
| case EVENT_METEREDNESS_CHANGED: |
| return getMeterednessChangedLog(data.ifield1, data.bfield1); |
| case EVENT_USER_STATE_REMOVED: |
| return getUserRemovedLog(data.ifield1); |
| case EVENT_RESTRICT_BG_CHANGED: |
| return getRestrictBackgroundChangedLog(data.bfield1, data.bfield2); |
| case EVENT_DEVICE_IDLE_MODE_ENABLED: |
| return getDeviceIdleModeEnabled(data.bfield1); |
| case EVENT_APP_IDLE_STATE_CHANGED: |
| return getAppIdleChangedLog(data.ifield1, data.bfield1); |
| case EVENT_APP_IDLE_WL_CHANGED: |
| return getAppIdleWlChangedLog(data.ifield1, data.bfield1); |
| case EVENT_PAROLE_STATE_CHANGED: |
| return getParoleStateChanged(data.bfield1); |
| case EVENT_TEMP_POWER_SAVE_WL_CHANGED: |
| return getTempPowerSaveWlChangedLog(data.ifield1, data.bfield1); |
| case EVENT_UID_FIREWALL_RULE_CHANGED: |
| return getUidFirewallRuleChangedLog(data.ifield1, data.ifield2, data.ifield3); |
| case EVENT_FIREWALL_CHAIN_ENABLED: |
| return getFirewallChainEnabledLog(data.ifield1, data.bfield1); |
| default: |
| return String.valueOf(data.type); |
| } |
| } |
| |
| private String formatDate(long millis) { |
| sDate.setTime(millis); |
| return sFormatter.format(sDate); |
| } |
| } |
| |
| public final static class Data { |
| int type; |
| long timeStamp; |
| |
| int ifield1; |
| int ifield2; |
| int ifield3; |
| long lfield1; |
| boolean bfield1; |
| boolean bfield2; |
| String sfield1; |
| |
| public void reset(){ |
| sfield1 = null; |
| } |
| } |
| } |