1 /*
2  * Copyright (C) 2022 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 com.android.server.power.stats.wakeups;
18 
19 import android.annotation.XmlRes;
20 import android.content.Context;
21 import android.content.res.XmlResourceParser;
22 import android.util.ArrayMap;
23 import android.util.ArraySet;
24 import android.util.IndentingPrintWriter;
25 import android.util.LongSparseArray;
26 
27 import com.android.internal.util.XmlUtils;
28 
29 import org.xmlpull.v1.XmlPullParser;
30 import org.xmlpull.v1.XmlPullParserException;
31 
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
36 
37 /**
38  * Parses irq_device_map.xml to store a mapping of devices that can send IRQs to the CPU to
39  * subsystems that represent some logical work happening on the device that could need an IRQ.
40  */
41 public class IrqDeviceMap {
42     private static final String TAG_IRQ_DEVICE_MAP = "irq-device-map";
43     private static final String TAG_DEVICE = "device";
44     private static final String TAG_SUBSYSTEM = "subsystem";
45     private static final String ATTR_NAME = "name";
46 
47     private static LongSparseArray<IrqDeviceMap> sInstanceMap = new LongSparseArray<>(1);
48 
49     private final ArrayMap<String, List<String>> mSubsystemsForDevice = new ArrayMap();
50 
IrqDeviceMap(XmlResourceParser parser)51     private IrqDeviceMap(XmlResourceParser parser) {
52         try {
53             XmlUtils.beginDocument(parser, TAG_IRQ_DEVICE_MAP);
54 
55             int type;
56             String currentDevice = null;
57             final ArraySet<String> subsystems = new ArraySet<>();
58 
59             while ((type = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {
60                 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_DEVICE)) {
61                     currentDevice = parser.getAttributeValue(null, ATTR_NAME);
62                 }
63                 if (currentDevice != null && type == XmlPullParser.END_TAG
64                         && parser.getName().equals(TAG_DEVICE)) {
65                     final int n = subsystems.size();
66                     if (n > 0) {
67                         mSubsystemsForDevice.put(currentDevice,
68                                 Collections.unmodifiableList(new ArrayList<>(subsystems)));
69                     }
70                     subsystems.clear();
71                     currentDevice = null;
72                 }
73                 if (currentDevice != null && type == XmlPullParser.START_TAG
74                         && parser.getName().equals(TAG_SUBSYSTEM)) {
75                     parser.next();
76                     if (parser.getEventType() == XmlPullParser.TEXT) {
77                         subsystems.add(parser.getText());
78                     }
79                 }
80                 parser.next();
81             }
82         } catch (XmlPullParserException e) {
83             throw new RuntimeException(e);
84         } catch (IOException e) {
85             throw new RuntimeException(e);
86         } finally {
87             parser.close();
88         }
89     }
90 
91     /**
92      * Returns an instance of IrqDeviceMap initialzed with the given context and xml resource.
93      * The xml resource should describe the mapping in a way similar to
94      * core/res/res/xml/irq_device_map.xml.
95      */
getInstance(Context context, @XmlRes int resId)96     public static IrqDeviceMap getInstance(Context context, @XmlRes int resId) {
97         synchronized (IrqDeviceMap.class) {
98             final int idx = sInstanceMap.indexOfKey(resId);
99             if (idx >= 0) {
100                 return sInstanceMap.valueAt(idx);
101             }
102         }
103         final XmlResourceParser parser = context.getResources().getXml(resId);
104         final IrqDeviceMap irqDeviceMap = new IrqDeviceMap(parser);
105         synchronized (IrqDeviceMap.class) {
106             sInstanceMap.put(resId, irqDeviceMap);
107         }
108         return irqDeviceMap;
109     }
110 
getSubsystemsForDevice(String device)111     List<String> getSubsystemsForDevice(String device) {
112         return mSubsystemsForDevice.get(device);
113     }
114 
dump(IndentingPrintWriter pw)115     void dump(IndentingPrintWriter pw) {
116         pw.println("Irq device map:");
117         pw.increaseIndent();
118 
119         final LongSparseArray<IrqDeviceMap> instanceMap;
120         synchronized (IrqDeviceMap.class) {
121             instanceMap = sInstanceMap;
122         }
123         final int idx = instanceMap.indexOfValue(this);
124         final String res = (idx >= 0) ? ("0x" + Long.toHexString(instanceMap.keyAt(idx))) : null;
125         pw.println("Loaded from xml resource: " + res);
126 
127         pw.println("Map:");
128         pw.increaseIndent();
129         for (int i = 0; i < mSubsystemsForDevice.size(); i++) {
130             pw.print(mSubsystemsForDevice.keyAt(i) + ": ");
131             pw.println(mSubsystemsForDevice.valueAt(i));
132         }
133         pw.decreaseIndent();
134 
135         pw.decreaseIndent();
136     }
137 }
138