Loading services/core/java/com/android/server/ExtconStateObserver.java 0 → 100644 +96 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.FileUtils; import android.util.Slog; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; /** * A specialized ExtconUEventObserver that on receiving a {@link UEvent} calls {@link * #updateState(ExtconInfo, String, S)} with the value of{@link #parseState(ExtconInfo, String)}. * * @param <S> the type of state to parse and update * @hide */ public abstract class ExtconStateObserver<S> extends ExtconUEventObserver { private static final String TAG = "ExtconStateObserver"; private static final boolean LOG = false; /** * Parses the current state from the state file for {@code extconInfo} and calls {@link * #updateState(ExtconInfo, String, Object)} * * @param extconInfo the extconInfo to update state for * @see #parseState(ExtconInfo, String) * @see ExtconInfo#getStatePath() */ public void updateStateFromFile(ExtconInfo extconInfo) { String statePath = extconInfo.getStatePath(); try { S state = parseState( extconInfo, FileUtils.readTextFile(new File(statePath), 0, null).trim()); if (state != null) { updateState(extconInfo, extconInfo.getName(), state); } } catch (FileNotFoundException e) { Slog.w(TAG, statePath + " not found while attempting to determine initial state", e); } catch (IOException e) { Slog.e( TAG, "Error reading " + statePath + " while attempting to determine initial state ", e); } } @Override public void onUEvent(ExtconInfo extconInfo, UEvent event) { if (LOG) Slog.d(TAG, extconInfo.getName() + " UEVENT: " + event); String name = event.get("NAME"); S state = parseState(extconInfo, event.get("STATE")); if (state != null) { updateState(extconInfo, name, state); } } /** * Subclasses of ExtconStateObserver should override this method update state for {@code * exconInfo} from an {@code UEvent}. * * @param extconInfo the external connection * @param eventName the {@code NAME} of the {@code UEvent} * @param state the{@code STATE} as parsed by {@link #parseState(ExtconInfo, String)}. */ public abstract void updateState(ExtconInfo extconInfo, String eventName, @NonNull S state); /** * Subclasses of ExtconStateObserver should override this method to parse the {@code STATE} from * an UEvent. * * @param extconInfo that matches the {@code DEVPATH} of {@code event} * @param state the {@code STATE} from a {@code UEvent}. * @return the parsed state. Return null if the state can not be parsed. */ @Nullable public abstract S parseState(ExtconInfo extconInfo, String state); } services/core/java/com/android/server/ExtconUEventObserver.java 0 → 100644 +128 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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; import android.annotation.Nullable; import android.os.UEventObserver; import android.util.ArrayMap; import android.util.Slog; import java.io.File; import java.io.IOException; import java.util.Locale; import java.util.Map; /** * A specialized UEventObserver that receives UEvents from the kernel for devices in the {@code * /sys/class/extcon}. directory * * <p>Subclass ExtconUEventObserver, implementing {@link #onUEvent(ExtconInfo, UEvent)}, then call * startObserving() with a ExtconInfo to observe. The UEvent thread will then call your onUEvent() * method when a UEvent occurs that matches the path of your ExtconInfos. * * <p>Call stopObserving() to stop receiving UEvents. * * <p>There is only one UEvent thread per process, even if that process has multiple UEventObserver * subclass instances. The UEvent thread starts when the startObserving() is called for the first * time in that process. Once started the UEvent thread will not stop (although it can stop * notifying UEventObserver's via stopObserving()). * * <p> * * @hide */ public abstract class ExtconUEventObserver extends UEventObserver { private static final String TAG = "ExtconUEventObserver"; private static final boolean LOG = false; private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>(); @Override public final void onUEvent(UEvent event) { String devPath = event.get("DEVPATH"); ExtconInfo info = mExtconInfos.get(devPath); if (info != null) { onUEvent(info, event); } else { Slog.w(TAG, "No match found for DEVPATH of " + event + " in " + mExtconInfos); } } /** * Subclasses of ExtconUEventObserver should override this method to handle UEvents. * * @param extconInfo that matches the {@code DEVPATH} of {@code event} * @param event the event */ protected abstract void onUEvent(ExtconInfo extconInfo, UEvent event); /** Starts observing {@link ExtconInfo#getDevicePath()}. */ public void startObserving(ExtconInfo extconInfo) { mExtconInfos.put(extconInfo.getDevicePath(), extconInfo); if (LOG) Slog.v(TAG, "Observing " + extconInfo.getDevicePath()); startObserving("DEVPATH=" + extconInfo.getDevicePath()); } /** An External Connection to watch. */ public static final class ExtconInfo { private static final String TAG = "ExtconInfo"; private final String mName; public ExtconInfo(String name) { mName = name; } /** The name of the external connection */ public String getName() { return mName; } /** * The path to the device for this external connection. * * <p><b>NOTE</b> getting this path involves resolving a symlink. * * @return the device path, or null if it not found. */ @Nullable public String getDevicePath() { try { String extconPath = String.format(Locale.US, "/sys/class/extcon/%s", mName); File devPath = new File(extconPath); if (devPath.exists()) { String canonicalPath = devPath.getCanonicalPath(); int start = canonicalPath.indexOf("/devices"); return canonicalPath.substring(start); } return null; } catch (IOException e) { Slog.e(TAG, "Could not get the extcon device path for " + mName, e); return null; } } /** The path to the state file */ public String getStatePath() { return String.format(Locale.US, "/sys/class/extcon/%s/state", mName); } } /** Does the {@link /sys/class/extcon} directory exist */ public static boolean extconExists() { File extconDir = new File("/sys/class/extcon"); return extconDir.exists() && extconDir.isDirectory(); } } Loading
services/core/java/com/android/server/ExtconStateObserver.java 0 → 100644 +96 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.FileUtils; import android.util.Slog; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; /** * A specialized ExtconUEventObserver that on receiving a {@link UEvent} calls {@link * #updateState(ExtconInfo, String, S)} with the value of{@link #parseState(ExtconInfo, String)}. * * @param <S> the type of state to parse and update * @hide */ public abstract class ExtconStateObserver<S> extends ExtconUEventObserver { private static final String TAG = "ExtconStateObserver"; private static final boolean LOG = false; /** * Parses the current state from the state file for {@code extconInfo} and calls {@link * #updateState(ExtconInfo, String, Object)} * * @param extconInfo the extconInfo to update state for * @see #parseState(ExtconInfo, String) * @see ExtconInfo#getStatePath() */ public void updateStateFromFile(ExtconInfo extconInfo) { String statePath = extconInfo.getStatePath(); try { S state = parseState( extconInfo, FileUtils.readTextFile(new File(statePath), 0, null).trim()); if (state != null) { updateState(extconInfo, extconInfo.getName(), state); } } catch (FileNotFoundException e) { Slog.w(TAG, statePath + " not found while attempting to determine initial state", e); } catch (IOException e) { Slog.e( TAG, "Error reading " + statePath + " while attempting to determine initial state ", e); } } @Override public void onUEvent(ExtconInfo extconInfo, UEvent event) { if (LOG) Slog.d(TAG, extconInfo.getName() + " UEVENT: " + event); String name = event.get("NAME"); S state = parseState(extconInfo, event.get("STATE")); if (state != null) { updateState(extconInfo, name, state); } } /** * Subclasses of ExtconStateObserver should override this method update state for {@code * exconInfo} from an {@code UEvent}. * * @param extconInfo the external connection * @param eventName the {@code NAME} of the {@code UEvent} * @param state the{@code STATE} as parsed by {@link #parseState(ExtconInfo, String)}. */ public abstract void updateState(ExtconInfo extconInfo, String eventName, @NonNull S state); /** * Subclasses of ExtconStateObserver should override this method to parse the {@code STATE} from * an UEvent. * * @param extconInfo that matches the {@code DEVPATH} of {@code event} * @param state the {@code STATE} from a {@code UEvent}. * @return the parsed state. Return null if the state can not be parsed. */ @Nullable public abstract S parseState(ExtconInfo extconInfo, String state); }
services/core/java/com/android/server/ExtconUEventObserver.java 0 → 100644 +128 −0 Original line number Diff line number Diff line /* * Copyright (C) 2018 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; import android.annotation.Nullable; import android.os.UEventObserver; import android.util.ArrayMap; import android.util.Slog; import java.io.File; import java.io.IOException; import java.util.Locale; import java.util.Map; /** * A specialized UEventObserver that receives UEvents from the kernel for devices in the {@code * /sys/class/extcon}. directory * * <p>Subclass ExtconUEventObserver, implementing {@link #onUEvent(ExtconInfo, UEvent)}, then call * startObserving() with a ExtconInfo to observe. The UEvent thread will then call your onUEvent() * method when a UEvent occurs that matches the path of your ExtconInfos. * * <p>Call stopObserving() to stop receiving UEvents. * * <p>There is only one UEvent thread per process, even if that process has multiple UEventObserver * subclass instances. The UEvent thread starts when the startObserving() is called for the first * time in that process. Once started the UEvent thread will not stop (although it can stop * notifying UEventObserver's via stopObserving()). * * <p> * * @hide */ public abstract class ExtconUEventObserver extends UEventObserver { private static final String TAG = "ExtconUEventObserver"; private static final boolean LOG = false; private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>(); @Override public final void onUEvent(UEvent event) { String devPath = event.get("DEVPATH"); ExtconInfo info = mExtconInfos.get(devPath); if (info != null) { onUEvent(info, event); } else { Slog.w(TAG, "No match found for DEVPATH of " + event + " in " + mExtconInfos); } } /** * Subclasses of ExtconUEventObserver should override this method to handle UEvents. * * @param extconInfo that matches the {@code DEVPATH} of {@code event} * @param event the event */ protected abstract void onUEvent(ExtconInfo extconInfo, UEvent event); /** Starts observing {@link ExtconInfo#getDevicePath()}. */ public void startObserving(ExtconInfo extconInfo) { mExtconInfos.put(extconInfo.getDevicePath(), extconInfo); if (LOG) Slog.v(TAG, "Observing " + extconInfo.getDevicePath()); startObserving("DEVPATH=" + extconInfo.getDevicePath()); } /** An External Connection to watch. */ public static final class ExtconInfo { private static final String TAG = "ExtconInfo"; private final String mName; public ExtconInfo(String name) { mName = name; } /** The name of the external connection */ public String getName() { return mName; } /** * The path to the device for this external connection. * * <p><b>NOTE</b> getting this path involves resolving a symlink. * * @return the device path, or null if it not found. */ @Nullable public String getDevicePath() { try { String extconPath = String.format(Locale.US, "/sys/class/extcon/%s", mName); File devPath = new File(extconPath); if (devPath.exists()) { String canonicalPath = devPath.getCanonicalPath(); int start = canonicalPath.indexOf("/devices"); return canonicalPath.substring(start); } return null; } catch (IOException e) { Slog.e(TAG, "Could not get the extcon device path for " + mName, e); return null; } } /** The path to the state file */ public String getStatePath() { return String.format(Locale.US, "/sys/class/extcon/%s/state", mName); } } /** Does the {@link /sys/class/extcon} directory exist */ public static boolean extconExists() { File extconDir = new File("/sys/class/extcon"); return extconDir.exists() && extconDir.isDirectory(); } }