content: /*
   * Copyright (C) 2014 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.ethernet;
  
  import android.content.Context;
  import android.content.pm.PackageManager;
  import android.net.IEthernetManager;
  import android.net.IEthernetServiceListener;
  import android.net.IpConfiguration;
  import android.os.Binder;
  import android.os.Handler;
  import android.os.HandlerThread;
  import android.os.RemoteException;
  import android.util.Log;
  import android.util.PrintWriterPrinter;
  
  import com.android.internal.util.IndentingPrintWriter;
  
  import java.io.FileDescriptor;
  import java.io.PrintWriter;
  import java.util.concurrent.atomic.AtomicBoolean;
  
  /**
   * EthernetServiceImpl handles remote Ethernet operation requests by implementing
   * the IEthernetManager interface.
   */
  public class EthernetServiceImpl extends IEthernetManager.Stub {
      private static final String TAG = "EthernetServiceImpl";
  
      private final Context mContext;
      private final AtomicBoolean mStarted = new AtomicBoolean(false);
  
      private Handler mHandler;
      private EthernetTracker mTracker;
  
      public EthernetServiceImpl(Context context) {
          mContext = context;
      }
  
      private void enforceAccessPermission() {
          mContext.enforceCallingOrSelfPermission(
                  android.Manifest.permission.ACCESS_NETWORK_STATE,
                  "EthernetService");
      }
  
      private void enforceConnectivityInternalPermission() {
          mContext.enforceCallingOrSelfPermission(
                  android.Manifest.permission.CONNECTIVITY_INTERNAL,
                  "ConnectivityService");
      }
  
      private void enforceUseRestrictedNetworksPermission() {
          mContext.enforceCallingOrSelfPermission(
                  android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS,
                  "ConnectivityService");
      }
  
      private boolean checkUseRestrictedNetworksPermission() {
          return mContext.checkCallingOrSelfPermission(
                  android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS)
                  == PackageManager.PERMISSION_GRANTED;
      }
  
      public void start() {
          Log.i(TAG, "Starting Ethernet service");
  
          HandlerThread handlerThread = new HandlerThread("EthernetServiceThread");
          handlerThread.start();
          mHandler = new Handler(handlerThread.getLooper());
  
          mTracker = new EthernetTracker(mContext, mHandler);
          mTracker.start();
  
          mStarted.set(true);
      }
  
      @Override
      public String[] getAvailableInterfaces() throws RemoteException {
          return mTracker.getInterfaces(checkUseRestrictedNetworksPermission());
      }
  
      /**
       * Get Ethernet configuration
       * @return the Ethernet Configuration, contained in {@link IpConfiguration}.
       */
      @Override
      public IpConfiguration getConfiguration(String iface) {
          enforceAccessPermission();
  
          if (mTracker.isRestrictedInterface(iface)) {
              enforceUseRestrictedNetworksPermission();
          }
  
          return new IpConfiguration(mTracker.getIpConfiguration(iface));
      }
  
      /**
       * Set Ethernet configuration
       */
      @Override
      public void setConfiguration(String iface, IpConfiguration config) {
          if (!mStarted.get()) {
              Log.w(TAG, "System isn't ready enough to change ethernet configuration");
          }
  
          enforceConnectivityInternalPermission();
  
          if (mTracker.isRestrictedInterface(iface)) {
              enforceUseRestrictedNetworksPermission();
          }
  
          // TODO: this does not check proxy settings, gateways, etc.
          // Fix this by making IpConfiguration a complete representation of static configuration.
          mTracker.updateIpConfiguration(iface, new IpConfiguration(config));
      }
  
      /**
       * Indicates whether given interface is available.
       */
      @Override
      public boolean isAvailable(String iface) {
          enforceAccessPermission();
  
          if (mTracker.isRestrictedInterface(iface)) {
              enforceUseRestrictedNetworksPermission();
          }
  
          return mTracker.isTrackingInterface(iface);
      }
  
      /**
       * Adds a listener.
       * @param listener A {@link IEthernetServiceListener} to add.
       */
      public void addListener(IEthernetServiceListener listener) {
          if (listener == null) {
              throw new IllegalArgumentException("listener must not be null");
          }
          enforceAccessPermission();
          mTracker.addListener(listener, checkUseRestrictedNetworksPermission());
      }
  
      /**
       * Removes a listener.
       * @param listener A {@link IEthernetServiceListener} to remove.
       */
      public void removeListener(IEthernetServiceListener listener) {
          if (listener == null) {
              throw new IllegalArgumentException("listener must not be null");
          }
          enforceAccessPermission();
          mTracker.removeListener(listener);
      }
  
      @Override
      protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
          final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
          if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                  != PackageManager.PERMISSION_GRANTED) {
              pw.println("Permission Denial: can't dump EthernetService from pid="
                      + Binder.getCallingPid()
                      + ", uid=" + Binder.getCallingUid());
              return;
          }
  
          pw.println("Current Ethernet state: ");
          pw.increaseIndent();
          mTracker.dump(fd, pw, args);
          pw.decreaseIndent();
  
          pw.println("Handler:");
          pw.increaseIndent();
          mHandler.dump(new PrintWriterPrinter(pw), "EthernetServiceImpl");
          pw.decreaseIndent();
      }
  }
  